aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/api.txt133
-rw-r--r--src/nvim/api/vim.c200
-rw-r--r--src/nvim/api/vimscript.c229
-rw-r--r--src/nvim/ex_docmd.c122
-rw-r--r--test/functional/api/vim_spec.lua122
5 files changed, 483 insertions, 323 deletions
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 7fc94ff76b..8e2bc1f036 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -1361,69 +1361,6 @@ nvim_out_write({str}) *nvim_out_write()*
Parameters: ~
{str} Message
-nvim_parse_cmd({str}, {opts}) *nvim_parse_cmd()*
- Parse command line.
-
- Doesn't check the validity of command arguments.
-
- Attributes: ~
- |api-fast|
-
- Parameters: ~
- {str} Command line string to parse. Cannot contain "\n".
- {opts} Optional parameters. Reserved for future use.
-
- Return: ~
- Dictionary containing command information, with these
- keys:
- • cmd: (string) Command name.
- • line1: (number) Starting line of command range. Only
- applicable if command can take a range.
- • line2: (number) Final line of command range. Only
- applicable if command can take a range.
- • bang: (boolean) Whether command contains a bang (!)
- modifier.
- • args: (array) Command arguments.
- • addr: (string) Value of |:command-addr|. Uses short
- name.
- • nargs: (string) Value of |:command-nargs|.
- • nextcmd: (string) Next command if there are multiple
- commands separated by a |:bar|. Empty if there isn't a
- next command.
- • magic: (dictionary) Which characters have special
- meaning in the command arguments.
- • file: (boolean) The command expands filenames. Which
- means characters such as "%", "#" and wildcards are
- expanded.
- • bar: (boolean) The "|" character is treated as a
- command separator and the double quote character (")
- is treated as the start of a comment.
-
- • mods: (dictionary) |:command-modifiers|.
- • silent: (boolean) |:silent|.
- • emsg_silent: (boolean) |:silent!|.
- • sandbox: (boolean) |:sandbox|.
- • noautocmd: (boolean) |:noautocmd|.
- • browse: (boolean) |:browse|.
- • confirm: (boolean) |:confirm|.
- • hide: (boolean) |:hide|.
- • keepalt: (boolean) |:keepalt|.
- • keepjumps: (boolean) |:keepjumps|.
- • keepmarks: (boolean) |:keepmarks|.
- • keeppatterns: (boolean) |:keeppatterns|.
- • lockmarks: (boolean) |:lockmarks|.
- • noswapfile: (boolean) |:noswapfile|.
- • tab: (integer) |:tab|.
- • verbose: (integer) |:verbose|.
- • vertical: (boolean) |:vertical|.
- • split: (string) Split modifier string, is an empty
- string when there's no split modifier. If there is a
- split modifier it can be one of:
- • "aboveleft": |:aboveleft|.
- • "belowright": |:belowright|.
- • "topleft": |:topleft|.
- • "botright": |:botright|.
-
nvim_paste({data}, {crlf}, {phase}) *nvim_paste()*
Pastes at cursor, in any mode.
@@ -1822,6 +1759,76 @@ nvim_exec({src}, {output}) *nvim_exec()*
|execute()|
|nvim_command()|
+nvim_parse_cmd({str}, {opts}) *nvim_parse_cmd()*
+ Parse command line.
+
+ Doesn't check the validity of command arguments.
+
+ Attributes: ~
+ |api-fast|
+
+ Parameters: ~
+ {str} Command line string to parse. Cannot contain "\n".
+ {opts} Optional parameters. Reserved for future use.
+
+ Return: ~
+ Dictionary containing command information, with these
+ keys:
+ • cmd: (string) Command name.
+ • range: (number) Number of items in the command
+ |<range>|. Can be 0, 1 or 2.
+ • line1: (number) Starting line of command |<range>|. -1
+ if command cannot take a range. |<line1>|
+ • line2: (number) Final line of command |<range>|. -1 if
+ command cannot take a range. |<line2>|
+ • count: (number) Any |<count>| that was supplied to the
+ command. -1 if command cannot take a count.
+ • reg: (number) The optional command |<register>|, if
+ specified. Empty string if not specified or if command
+ cannot take a register.
+ • bang: (boolean) Whether command contains a |<bang>| (!)
+ modifier.
+ • args: (array) Command arguments.
+ • addr: (string) Value of |:command-addr|. Uses short
+ name.
+ • nargs: (string) Value of |:command-nargs|.
+ • nextcmd: (string) Next command if there are multiple
+ commands separated by a |:bar|. Empty if there isn't a
+ next command.
+ • magic: (dictionary) Which characters have special
+ meaning in the command arguments.
+ • file: (boolean) The command expands filenames. Which
+ means characters such as "%", "#" and wildcards are
+ expanded.
+ • bar: (boolean) The "|" character is treated as a
+ command separator and the double quote character (")
+ is treated as the start of a comment.
+
+ • mods: (dictionary) |:command-modifiers|.
+ • silent: (boolean) |:silent|.
+ • emsg_silent: (boolean) |:silent!|.
+ • sandbox: (boolean) |:sandbox|.
+ • noautocmd: (boolean) |:noautocmd|.
+ • browse: (boolean) |:browse|.
+ • confirm: (boolean) |:confirm|.
+ • hide: (boolean) |:hide|.
+ • keepalt: (boolean) |:keepalt|.
+ • keepjumps: (boolean) |:keepjumps|.
+ • keepmarks: (boolean) |:keepmarks|.
+ • keeppatterns: (boolean) |:keeppatterns|.
+ • lockmarks: (boolean) |:lockmarks|.
+ • noswapfile: (boolean) |:noswapfile|.
+ • tab: (integer) |:tab|.
+ • verbose: (integer) |:verbose|.
+ • vertical: (boolean) |:vertical|.
+ • split: (string) Split modifier string, is an empty
+ string when there's no split modifier. If there is a
+ split modifier it can be one of:
+ • "aboveleft": |:aboveleft|.
+ • "belowright": |:belowright|.
+ • "topleft": |:topleft|.
+ • "botright": |:botright|.
+
*nvim_parse_expression()*
nvim_parse_expression({expr}, {flags}, {highlight})
Parse a VimL expression.
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index d9ab097316..3f0cfb82a6 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -2461,203 +2461,3 @@ void nvim_del_user_command(String name, Error *err)
{
nvim_buf_del_user_command(-1, name, err);
}
-
-/// Parse command line.
-///
-/// Doesn't check the validity of command arguments.
-///
-/// @param str Command line string to parse. Cannot contain "\n".
-/// @param opts Optional parameters. Reserved for future use.
-/// @param[out] err Error details, if any.
-/// @return Dictionary containing command information, with these keys:
-/// - cmd: (string) Command name.
-/// - line1: (number) Starting line of command range. Only applicable if command can take a
-/// range.
-/// - line2: (number) Final line of command range. Only applicable if command can take a
-/// range.
-/// - bang: (boolean) Whether command contains a bang (!) modifier.
-/// - args: (array) Command arguments.
-/// - addr: (string) Value of |:command-addr|. Uses short name.
-/// - nargs: (string) Value of |:command-nargs|.
-/// - nextcmd: (string) Next command if there are multiple commands separated by a |:bar|.
-/// Empty if there isn't a next command.
-/// - magic: (dictionary) Which characters have special meaning in the command arguments.
-/// - file: (boolean) The command expands filenames. Which means characters such as "%",
-/// "#" and wildcards are expanded.
-/// - bar: (boolean) The "|" character is treated as a command separator and the double
-/// quote character (\") is treated as the start of a comment.
-/// - mods: (dictionary) |:command-modifiers|.
-/// - silent: (boolean) |:silent|.
-/// - emsg_silent: (boolean) |:silent!|.
-/// - sandbox: (boolean) |:sandbox|.
-/// - noautocmd: (boolean) |:noautocmd|.
-/// - browse: (boolean) |:browse|.
-/// - confirm: (boolean) |:confirm|.
-/// - hide: (boolean) |:hide|.
-/// - keepalt: (boolean) |:keepalt|.
-/// - keepjumps: (boolean) |:keepjumps|.
-/// - keepmarks: (boolean) |:keepmarks|.
-/// - keeppatterns: (boolean) |:keeppatterns|.
-/// - lockmarks: (boolean) |:lockmarks|.
-/// - noswapfile: (boolean) |:noswapfile|.
-/// - tab: (integer) |:tab|.
-/// - verbose: (integer) |:verbose|.
-/// - vertical: (boolean) |:vertical|.
-/// - split: (string) Split modifier string, is an empty string when there's no split
-/// modifier. If there is a split modifier it can be one of:
-/// - "aboveleft": |:aboveleft|.
-/// - "belowright": |:belowright|.
-/// - "topleft": |:topleft|.
-/// - "botright": |:botright|.
-Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
- FUNC_API_SINCE(10) FUNC_API_FAST
-{
- Dictionary result = ARRAY_DICT_INIT;
-
- if (opts.size > 0) {
- api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
- return result;
- }
-
- // Parse command line
- exarg_T ea;
- CmdParseInfo cmdinfo;
- char_u *cmdline = (char_u *)string_to_cstr(str);
-
- if (!parse_cmdline(cmdline, &ea, &cmdinfo)) {
- api_set_error(err, kErrorTypeException, "Error while parsing command line");
- goto end;
- }
-
- // Parse arguments
- Array args = ARRAY_DICT_INIT;
- size_t length = STRLEN(ea.arg);
-
- // For nargs = 1 or '?', pass the entire argument list as a single argument,
- // otherwise split arguments by whitespace.
- if (ea.argt & EX_NOSPC) {
- if (*ea.arg != NUL) {
- ADD(args, STRING_OBJ(cstrn_to_string((char *)ea.arg, length)));
- }
- } else {
- size_t end = 0;
- size_t len = 0;
- char *buf = xcalloc(length, sizeof(char));
- bool done = false;
-
- while (!done) {
- done = uc_split_args_iter(ea.arg, length, &end, buf, &len);
- if (len > 0) {
- ADD(args, STRING_OBJ(cstrn_to_string(buf, len)));
- }
- }
-
- xfree(buf);
- }
-
- if (ea.cmdidx == CMD_USER) {
- PUT(result, "cmd", CSTR_TO_OBJ((char *)USER_CMD(ea.useridx)->uc_name));
- } else if (ea.cmdidx == CMD_USER_BUF) {
- PUT(result, "cmd", CSTR_TO_OBJ((char *)USER_CMD_GA(&curbuf->b_ucmds, ea.useridx)->uc_name));
- } else {
- PUT(result, "cmd", CSTR_TO_OBJ((char *)get_command_name(NULL, ea.cmdidx)));
- }
- PUT(result, "line1", INTEGER_OBJ(ea.line1));
- PUT(result, "line2", INTEGER_OBJ(ea.line2));
- PUT(result, "bang", BOOLEAN_OBJ(ea.forceit));
- PUT(result, "args", ARRAY_OBJ(args));
-
- char nargs[2];
- if (ea.argt & EX_EXTRA) {
- if (ea.argt & EX_NOSPC) {
- if (ea.argt & EX_NEEDARG) {
- nargs[0] = '1';
- } else {
- nargs[0] = '?';
- }
- } else if (ea.argt & EX_NEEDARG) {
- nargs[0] = '+';
- } else {
- nargs[0] = '*';
- }
- } else {
- nargs[0] = '0';
- }
- nargs[1] = '\0';
- PUT(result, "nargs", CSTR_TO_OBJ(nargs));
-
- const char *addr;
- switch (ea.addr_type) {
- case ADDR_LINES:
- addr = "line";
- break;
- case ADDR_ARGUMENTS:
- addr = "arg";
- break;
- case ADDR_BUFFERS:
- addr = "buf";
- break;
- case ADDR_LOADED_BUFFERS:
- addr = "load";
- break;
- case ADDR_WINDOWS:
- addr = "win";
- break;
- case ADDR_TABS:
- addr = "tab";
- break;
- case ADDR_QUICKFIX:
- addr = "qf";
- break;
- case ADDR_NONE:
- addr = "none";
- break;
- default:
- addr = "?";
- break;
- }
- PUT(result, "addr", CSTR_TO_OBJ(addr));
- PUT(result, "nextcmd", CSTR_TO_OBJ((char *)ea.nextcmd));
-
- Dictionary mods = ARRAY_DICT_INIT;
- PUT(mods, "silent", BOOLEAN_OBJ(cmdinfo.silent));
- PUT(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.emsg_silent));
- PUT(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.sandbox));
- PUT(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.noautocmd));
- PUT(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.tab));
- PUT(mods, "verbose", INTEGER_OBJ(cmdinfo.verbose));
- PUT(mods, "browse", BOOLEAN_OBJ(cmdinfo.cmdmod.browse));
- PUT(mods, "confirm", BOOLEAN_OBJ(cmdinfo.cmdmod.confirm));
- PUT(mods, "hide", BOOLEAN_OBJ(cmdinfo.cmdmod.hide));
- PUT(mods, "keepalt", BOOLEAN_OBJ(cmdinfo.cmdmod.keepalt));
- PUT(mods, "keepjumps", BOOLEAN_OBJ(cmdinfo.cmdmod.keepjumps));
- PUT(mods, "keepmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.keepmarks));
- PUT(mods, "keeppatterns", BOOLEAN_OBJ(cmdinfo.cmdmod.keeppatterns));
- PUT(mods, "lockmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.lockmarks));
- PUT(mods, "noswapfile", BOOLEAN_OBJ(cmdinfo.cmdmod.noswapfile));
- PUT(mods, "vertical", BOOLEAN_OBJ(cmdinfo.cmdmod.split & WSP_VERT));
-
- const char *split;
- if (cmdinfo.cmdmod.split & WSP_BOT) {
- split = "botright";
- } else if (cmdinfo.cmdmod.split & WSP_TOP) {
- split = "topleft";
- } else if (cmdinfo.cmdmod.split & WSP_BELOW) {
- split = "belowright";
- } else if (cmdinfo.cmdmod.split & WSP_ABOVE) {
- split = "aboveleft";
- } else {
- split = "";
- }
- PUT(mods, "split", CSTR_TO_OBJ(split));
-
- PUT(result, "mods", DICTIONARY_OBJ(mods));
-
- Dictionary magic = ARRAY_DICT_INIT;
- PUT(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file));
- PUT(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar));
- PUT(result, "magic", DICTIONARY_OBJ(magic));
-end:
- xfree(cmdline);
- return result;
-}
diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c
index 40ac0b8b64..99d1406cfb 100644
--- a/src/nvim/api/vimscript.c
+++ b/src/nvim/api/vimscript.c
@@ -16,6 +16,7 @@
#include "nvim/ex_cmds2.h"
#include "nvim/viml/parser/expressions.h"
#include "nvim/viml/parser/parser.h"
+#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "api/vimscript.c.generated.h"
@@ -736,3 +737,231 @@ Dictionary nvim_parse_expression(String expr, String flags, Boolean highlight, E
viml_parser_destroy(&pstate);
return ret;
}
+
+/// Parse command line.
+///
+/// Doesn't check the validity of command arguments.
+///
+/// @param str Command line string to parse. Cannot contain "\n".
+/// @param opts Optional parameters. Reserved for future use.
+/// @param[out] err Error details, if any.
+/// @return Dictionary containing command information, with these keys:
+/// - cmd: (string) Command name.
+/// - range: (number) Number of items in the command |<range>|. Can be 0, 1 or 2.
+/// - line1: (number) Starting line of command |<range>|. -1 if command cannot take a range.
+/// |<line1>|
+/// - line2: (number) Final line of command |<range>|. -1 if command cannot take a range.
+/// |<line2>|
+/// - count: (number) Any |<count>| that was supplied to the command. -1 if command cannot
+/// take a count.
+/// - reg: (number) The optional command |<register>|, if specified. Empty string if not
+/// specified or if command cannot take a register.
+/// - bang: (boolean) Whether command contains a |<bang>| (!) modifier.
+/// - args: (array) Command arguments.
+/// - addr: (string) Value of |:command-addr|. Uses short name.
+/// - nargs: (string) Value of |:command-nargs|.
+/// - nextcmd: (string) Next command if there are multiple commands separated by a |:bar|.
+/// Empty if there isn't a next command.
+/// - magic: (dictionary) Which characters have special meaning in the command arguments.
+/// - file: (boolean) The command expands filenames. Which means characters such as "%",
+/// "#" and wildcards are expanded.
+/// - bar: (boolean) The "|" character is treated as a command separator and the double
+/// quote character (\") is treated as the start of a comment.
+/// - mods: (dictionary) |:command-modifiers|.
+/// - silent: (boolean) |:silent|.
+/// - emsg_silent: (boolean) |:silent!|.
+/// - sandbox: (boolean) |:sandbox|.
+/// - noautocmd: (boolean) |:noautocmd|.
+/// - browse: (boolean) |:browse|.
+/// - confirm: (boolean) |:confirm|.
+/// - hide: (boolean) |:hide|.
+/// - keepalt: (boolean) |:keepalt|.
+/// - keepjumps: (boolean) |:keepjumps|.
+/// - keepmarks: (boolean) |:keepmarks|.
+/// - keeppatterns: (boolean) |:keeppatterns|.
+/// - lockmarks: (boolean) |:lockmarks|.
+/// - noswapfile: (boolean) |:noswapfile|.
+/// - tab: (integer) |:tab|.
+/// - verbose: (integer) |:verbose|. -1 when omitted.
+/// - vertical: (boolean) |:vertical|.
+/// - split: (string) Split modifier string, is an empty string when there's no split
+/// modifier. If there is a split modifier it can be one of:
+/// - "aboveleft": |:aboveleft|.
+/// - "belowright": |:belowright|.
+/// - "topleft": |:topleft|.
+/// - "botright": |:botright|.
+Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err)
+ FUNC_API_SINCE(10) FUNC_API_FAST
+{
+ Dictionary result = ARRAY_DICT_INIT;
+
+ if (opts.size > 0) {
+ api_set_error(err, kErrorTypeValidation, "opts dict isn't empty");
+ return result;
+ }
+
+ // Parse command line
+ exarg_T ea;
+ CmdParseInfo cmdinfo;
+ char_u *cmdline = (char_u *)string_to_cstr(str);
+
+ if (!parse_cmdline(cmdline, &ea, &cmdinfo)) {
+ api_set_error(err, kErrorTypeException, "Error while parsing command line");
+ goto end;
+ }
+
+ // Parse arguments
+ Array args = ARRAY_DICT_INIT;
+ size_t length = STRLEN(ea.arg);
+
+ // For nargs = 1 or '?', pass the entire argument list as a single argument,
+ // otherwise split arguments by whitespace.
+ if (ea.argt & EX_NOSPC) {
+ if (*ea.arg != NUL) {
+ ADD(args, STRING_OBJ(cstrn_to_string((char *)ea.arg, length)));
+ }
+ } else {
+ size_t end = 0;
+ size_t len = 0;
+ char *buf = xcalloc(length, sizeof(char));
+ bool done = false;
+
+ while (!done) {
+ done = uc_split_args_iter(ea.arg, length, &end, buf, &len);
+ if (len > 0) {
+ ADD(args, STRING_OBJ(cstrn_to_string(buf, len)));
+ }
+ }
+
+ xfree(buf);
+ }
+
+ ucmd_T *cmd = NULL;
+ if (ea.cmdidx == CMD_USER) {
+ cmd = USER_CMD(ea.useridx);
+ } else if (ea.cmdidx == CMD_USER_BUF) {
+ cmd = USER_CMD_GA(&curbuf->b_ucmds, ea.useridx);
+ }
+
+ if (cmd != NULL) {
+ PUT(result, "cmd", CSTR_TO_OBJ((char *)cmd->uc_name));
+ } else {
+ PUT(result, "cmd", CSTR_TO_OBJ((char *)get_command_name(NULL, ea.cmdidx)));
+ }
+
+ PUT(result, "range", INTEGER_OBJ(ea.addr_count));
+ PUT(result, "line1", INTEGER_OBJ((ea.argt & EX_RANGE) ? ea.line1 : -1));
+ PUT(result, "line2", INTEGER_OBJ((ea.argt & EX_RANGE) ? ea.line2 : -1));
+
+ if (ea.argt & EX_COUNT) {
+ if (ea.addr_count > 0 || cmd == NULL) {
+ PUT(result, "count", INTEGER_OBJ(ea.line2));
+ } else {
+ PUT(result, "count", INTEGER_OBJ(cmd->uc_def));
+ }
+ } else {
+ PUT(result, "count", INTEGER_OBJ(-1));
+ }
+
+ char reg[2];
+ reg[0] = (char)ea.regname;
+ reg[1] = '\0';
+ PUT(result, "reg", CSTR_TO_OBJ(reg));
+
+ PUT(result, "bang", BOOLEAN_OBJ(ea.forceit));
+ PUT(result, "args", ARRAY_OBJ(args));
+
+ char nargs[2];
+ if (ea.argt & EX_EXTRA) {
+ if (ea.argt & EX_NOSPC) {
+ if (ea.argt & EX_NEEDARG) {
+ nargs[0] = '1';
+ } else {
+ nargs[0] = '?';
+ }
+ } else if (ea.argt & EX_NEEDARG) {
+ nargs[0] = '+';
+ } else {
+ nargs[0] = '*';
+ }
+ } else {
+ nargs[0] = '0';
+ }
+ nargs[1] = '\0';
+ PUT(result, "nargs", CSTR_TO_OBJ(nargs));
+
+ const char *addr;
+ switch (ea.addr_type) {
+ case ADDR_LINES:
+ addr = "line";
+ break;
+ case ADDR_ARGUMENTS:
+ addr = "arg";
+ break;
+ case ADDR_BUFFERS:
+ addr = "buf";
+ break;
+ case ADDR_LOADED_BUFFERS:
+ addr = "load";
+ break;
+ case ADDR_WINDOWS:
+ addr = "win";
+ break;
+ case ADDR_TABS:
+ addr = "tab";
+ break;
+ case ADDR_QUICKFIX:
+ addr = "qf";
+ break;
+ case ADDR_NONE:
+ addr = "none";
+ break;
+ default:
+ addr = "?";
+ break;
+ }
+ PUT(result, "addr", CSTR_TO_OBJ(addr));
+ PUT(result, "nextcmd", CSTR_TO_OBJ((char *)ea.nextcmd));
+
+ Dictionary mods = ARRAY_DICT_INIT;
+ PUT(mods, "silent", BOOLEAN_OBJ(cmdinfo.silent));
+ PUT(mods, "emsg_silent", BOOLEAN_OBJ(cmdinfo.emsg_silent));
+ PUT(mods, "sandbox", BOOLEAN_OBJ(cmdinfo.sandbox));
+ PUT(mods, "noautocmd", BOOLEAN_OBJ(cmdinfo.noautocmd));
+ PUT(mods, "tab", INTEGER_OBJ(cmdinfo.cmdmod.tab));
+ PUT(mods, "verbose", INTEGER_OBJ(cmdinfo.verbose));
+ PUT(mods, "browse", BOOLEAN_OBJ(cmdinfo.cmdmod.browse));
+ PUT(mods, "confirm", BOOLEAN_OBJ(cmdinfo.cmdmod.confirm));
+ PUT(mods, "hide", BOOLEAN_OBJ(cmdinfo.cmdmod.hide));
+ PUT(mods, "keepalt", BOOLEAN_OBJ(cmdinfo.cmdmod.keepalt));
+ PUT(mods, "keepjumps", BOOLEAN_OBJ(cmdinfo.cmdmod.keepjumps));
+ PUT(mods, "keepmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.keepmarks));
+ PUT(mods, "keeppatterns", BOOLEAN_OBJ(cmdinfo.cmdmod.keeppatterns));
+ PUT(mods, "lockmarks", BOOLEAN_OBJ(cmdinfo.cmdmod.lockmarks));
+ PUT(mods, "noswapfile", BOOLEAN_OBJ(cmdinfo.cmdmod.noswapfile));
+ PUT(mods, "vertical", BOOLEAN_OBJ(cmdinfo.cmdmod.split & WSP_VERT));
+
+ const char *split;
+ if (cmdinfo.cmdmod.split & WSP_BOT) {
+ split = "botright";
+ } else if (cmdinfo.cmdmod.split & WSP_TOP) {
+ split = "topleft";
+ } else if (cmdinfo.cmdmod.split & WSP_BELOW) {
+ split = "belowright";
+ } else if (cmdinfo.cmdmod.split & WSP_ABOVE) {
+ split = "aboveleft";
+ } else {
+ split = "";
+ }
+ PUT(mods, "split", CSTR_TO_OBJ(split));
+
+ PUT(result, "mods", DICTIONARY_OBJ(mods));
+
+ Dictionary magic = ARRAY_DICT_INIT;
+ PUT(magic, "file", BOOLEAN_OBJ(cmdinfo.magic.file));
+ PUT(magic, "bar", BOOLEAN_OBJ(cmdinfo.magic.bar));
+ PUT(result, "magic", DICTIONARY_OBJ(magic));
+end:
+ xfree(cmdline);
+ return result;
+}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 4aa2ef6ae0..e06dd7e59c 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1240,7 +1240,7 @@ static void set_cmd_addr_type(exarg_T *eap, char_u *p)
}
}
-/// Set default command range based on the addr type of the command
+/// Set default command range for -range=% based on the addr type of the command
static void set_cmd_default_range(exarg_T *eap)
{
buf_T *buf;
@@ -1298,6 +1298,66 @@ static void set_cmd_default_range(exarg_T *eap)
}
}
+static void parse_register(exarg_T *eap)
+{
+ // Accept numbered register only when no count allowed (:put)
+ if ((eap->argt & EX_REGSTR)
+ && *eap->arg != NUL
+ // Do not allow register = for user commands
+ && (!IS_USER_CMDIDX(eap->cmdidx) || *eap->arg != '=')
+ && !((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg))) {
+ if (valid_yank_reg(*eap->arg, (eap->cmdidx != CMD_put
+ && !IS_USER_CMDIDX(eap->cmdidx)))) {
+ eap->regname = *eap->arg++;
+ // for '=' register: accept the rest of the line as an expression
+ if (eap->arg[-1] == '=' && eap->arg[0] != NUL) {
+ if (!eap->skip) {
+ set_expr_line(vim_strsave(eap->arg));
+ }
+ eap->arg += STRLEN(eap->arg);
+ }
+ eap->arg = skipwhite(eap->arg);
+ }
+ }
+}
+
+static int parse_count(exarg_T *eap, char **errormsg)
+{
+ // Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a
+ // count, it's a buffer name.
+ char *p;
+ long n;
+
+ if ((eap->argt & EX_COUNT) && ascii_isdigit(*eap->arg)
+ && (!(eap->argt & EX_BUFNAME) || *(p = (char *)skipdigits(eap->arg + 1)) == NUL
+ || ascii_iswhite(*p))) {
+ n = getdigits_long(&eap->arg, false, -1);
+ eap->arg = skipwhite(eap->arg);
+ if (n <= 0 && (eap->argt & EX_ZEROR) == 0) {
+ if (errormsg != NULL) {
+ *errormsg = _(e_zerocount);
+ }
+ return FAIL;
+ }
+ if (eap->addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3
+ eap->line2 = n;
+ if (eap->addr_count == 0) {
+ eap->addr_count = 1;
+ }
+ } else {
+ eap->line1 = eap->line2;
+ eap->line2 += n - 1;
+ eap->addr_count++;
+ // Be vi compatible: no error message for out of range.
+ if (eap->line2 > curbuf->b_ml.ml_line_count) {
+ eap->line2 = curbuf->b_ml.ml_line_count;
+ }
+ }
+ }
+
+ return OK;
+}
+
/// Parse command line and return information about the first command.
///
/// @return Success or failure
@@ -1345,6 +1405,8 @@ bool parse_cmdline(char_u *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo)
if (eap->verbose_save != -1) {
cmdinfo->verbose = p_verbose;
p_verbose = eap->verbose_save;
+ } else {
+ cmdinfo->verbose = -1;
}
cmdinfo->cmdmod = cmdmod;
cmdmod = save_cmdmod;
@@ -1422,6 +1484,12 @@ bool parse_cmdline(char_u *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo)
set_cmd_default_range(eap);
}
+ // Parse register and count
+ parse_register(eap);
+ if (parse_count(eap, NULL) == FAIL) {
+ return false;
+ }
+
// Remove leading whitespace and colon from next command
if (eap->nextcmd) {
eap->nextcmd = (char_u *)skip_colon_white((char *)eap->nextcmd, true);
@@ -1460,7 +1528,6 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
{
char *p;
linenr_T lnum;
- long n;
char *errormsg = NULL; // error message
char *after_modifier = NULL;
exarg_T ea;
@@ -1899,53 +1966,10 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
set_cmd_default_range(&ea);
}
- // accept numbered register only when no count allowed (:put)
- if ((ea.argt & EX_REGSTR)
- && *ea.arg != NUL
- // Do not allow register = for user commands
- && (!IS_USER_CMDIDX(ea.cmdidx) || *ea.arg != '=')
- && !((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg))) {
- if (valid_yank_reg(*ea.arg, (ea.cmdidx != CMD_put
- && !IS_USER_CMDIDX(ea.cmdidx)))) {
- ea.regname = *ea.arg++;
- // for '=' register: accept the rest of the line as an expression
- if (ea.arg[-1] == '=' && ea.arg[0] != NUL) {
- if (!ea.skip) {
- set_expr_line(vim_strsave(ea.arg));
- }
- ea.arg += STRLEN(ea.arg);
- }
- ea.arg = skipwhite(ea.arg);
- }
- }
-
- //
- // Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a
- // count, it's a buffer name.
- ///
- if ((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg)
- && (!(ea.argt & EX_BUFNAME) || *(p = (char *)skipdigits(ea.arg + 1)) == NUL
- || ascii_iswhite(*p))) {
- n = getdigits_long(&ea.arg, false, -1);
- ea.arg = skipwhite(ea.arg);
- if (n <= 0 && !ni && (ea.argt & EX_ZEROR) == 0) {
- errormsg = _(e_zerocount);
- goto doend;
- }
- if (ea.addr_type != ADDR_LINES) { // e.g. :buffer 2, :sleep 3
- ea.line2 = n;
- if (ea.addr_count == 0) {
- ea.addr_count = 1;
- }
- } else {
- ea.line1 = ea.line2;
- ea.line2 += n - 1;
- ++ea.addr_count;
- // Be vi compatible: no error message for out of range.
- if (ea.line2 > curbuf->b_ml.ml_line_count) {
- ea.line2 = curbuf->b_ml.ml_line_count;
- }
- }
+ // Parse register and count
+ parse_register(&ea);
+ if (parse_count(&ea, &errormsg) == FAIL) {
+ goto doend;
}
/*
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index e138e2cc38..7e54ae0248 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -3104,8 +3104,11 @@ describe('API', function()
cmd = 'echo',
args = { 'foo' },
bang = false,
- line1 = 1,
- line2 = 1,
+ line1 = -1,
+ line2 = -1,
+ range = 0,
+ count = -1,
+ reg = '',
addr = 'none',
magic = {
file = false,
@@ -3130,7 +3133,7 @@ describe('API', function()
vertical = false,
split = "",
tab = 0,
- verbose = 0
+ verbose = -1
}
}, meths.parse_cmd('echo foo', {}))
end)
@@ -3141,6 +3144,9 @@ describe('API', function()
bang = false,
line1 = 4,
line2 = 6,
+ range = 2,
+ count = -1,
+ reg = '',
addr = 'line',
magic = {
file = false,
@@ -3165,10 +3171,86 @@ describe('API', function()
vertical = false,
split = "",
tab = 0,
- verbose = 0
+ verbose = -1
}
}, meths.parse_cmd('4,6s/math.random/math.max/', {}))
end)
+ it('works with count', function()
+ eq({
+ cmd = 'buffer',
+ args = {},
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ range = 1,
+ count = 1,
+ reg = '',
+ addr = 'buf',
+ magic = {
+ file = false,
+ bar = true
+ },
+ nargs = '*',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ vertical = false,
+ split = "",
+ tab = 0,
+ verbose = -1
+ }
+ }, meths.parse_cmd('buffer 1', {}))
+ end)
+ it('works with register', function()
+ eq({
+ cmd = 'put',
+ args = {},
+ bang = false,
+ line1 = 1,
+ line2 = 1,
+ range = 0,
+ count = -1,
+ reg = '+',
+ addr = 'line',
+ magic = {
+ file = false,
+ bar = true
+ },
+ nargs = '0',
+ nextcmd = '',
+ mods = {
+ browse = false,
+ confirm = false,
+ emsg_silent = false,
+ hide = false,
+ keepalt = false,
+ keepjumps = false,
+ keepmarks = false,
+ keeppatterns = false,
+ lockmarks = false,
+ noautocmd = false,
+ noswapfile = false,
+ sandbox = false,
+ silent = false,
+ vertical = false,
+ split = "",
+ tab = 0,
+ verbose = -1
+ }
+ }, meths.parse_cmd('put +', {}))
+ end)
it('works with bang', function()
eq({
cmd = 'write',
@@ -3176,6 +3258,9 @@ describe('API', function()
bang = true,
line1 = 1,
line2 = 1,
+ range = 0,
+ count = -1,
+ reg = '',
addr = 'line',
magic = {
file = true,
@@ -3200,7 +3285,7 @@ describe('API', function()
vertical = false,
split = "",
tab = 0,
- verbose = 0
+ verbose = -1
},
}, meths.parse_cmd('w!', {}))
end)
@@ -3211,6 +3296,9 @@ describe('API', function()
bang = false,
line1 = 1,
line2 = 1,
+ range = 0,
+ count = -1,
+ reg = '',
addr = '?',
magic = {
file = true,
@@ -3247,6 +3335,9 @@ describe('API', function()
bang = true,
line1 = 4,
line2 = 6,
+ range = 2,
+ count = -1,
+ reg = '',
addr = 'line',
magic = {
file = false,
@@ -3271,7 +3362,7 @@ describe('API', function()
vertical = false,
split = "",
tab = 0,
- verbose = 0
+ verbose = -1
}
}, meths.parse_cmd('4,6MyCommand! test it', {}))
end)
@@ -3282,6 +3373,9 @@ describe('API', function()
bang = false,
line1 = 0,
line2 = 0,
+ range = 0,
+ count = -1,
+ reg = '',
addr = 'arg',
magic = {
file = true,
@@ -3306,7 +3400,7 @@ describe('API', function()
vertical = false,
split = "",
tab = 0,
- verbose = 0
+ verbose = -1
}
}, meths.parse_cmd('argadd a.txt | argadd b.txt', {}))
end)
@@ -3316,8 +3410,11 @@ describe('API', function()
cmd = 'MyCommand',
args = { 'test it' },
bang = false,
- line1 = 1,
- line2 = 1,
+ line1 = -1,
+ line2 = -1,
+ range = 0,
+ count = -1,
+ reg = '',
addr = 'none',
magic = {
file = false,
@@ -3342,7 +3439,7 @@ describe('API', function()
vertical = false,
split = "",
tab = 0,
- verbose = 0
+ verbose = -1
}
}, meths.parse_cmd('MyCommand test it', {}))
end)
@@ -3355,6 +3452,9 @@ describe('API', function()
bang = false,
line1 = 1,
line2 = 2,
+ range = 0,
+ count = -1,
+ reg = '',
addr = 'buf',
magic = {
file = false,
@@ -3379,7 +3479,7 @@ describe('API', function()
vertical = false,
split = "",
tab = 0,
- verbose = 0
+ verbose = -1
}
}, meths.parse_cmd('MyCommand', {}))
end)