diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/vim.c | 200 | ||||
-rw-r--r-- | src/nvim/api/vimscript.c | 229 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 122 |
3 files changed, 302 insertions, 249 deletions
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; } /* |