diff options
author | Bram Moolenaar <Bram@vim.org> | 2021-12-01 15:22:56 +0000 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2021-12-01 15:22:56 +0000 |
commit | e4eed8c6db693a9183b776032570ce2f89dcffb6 (patch) | |
tree | 8edb253b95ca4b3dcfa430b8c9b3c9151d6257d9 | |
parent | f0e496a85a89fb1fd21b6af6363dcfc276331110 (diff) |
patch 8.2.3716: Vim9: range without a command is not compiledv8.2.3716
Problem: Vim9: range without a command is not compiled.
Solution: Add the ISN_EXECRANGE byte code.
-rw-r--r-- | src/ex_docmd.c | 87 | ||||
-rw-r--r-- | src/proto/ex_docmd.pro | 3 | ||||
-rw-r--r-- | src/testdir/test_vim9_disassemble.vim | 19 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9.h | 1 | ||||
-rw-r--r-- | src/vim9compile.c | 44 | ||||
-rw-r--r-- | src/vim9execute.c | 25 |
7 files changed, 136 insertions, 45 deletions
diff --git a/src/ex_docmd.c b/src/ex_docmd.c index d4863115d8..439a78cedc 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -1977,43 +1977,7 @@ do_one_cmd( */ if (ea.skip) // skip this if inside :if goto doend; - if ((*ea.cmd == '|' || (exmode_active && ea.line1 != ea.line2)) -#ifdef FEAT_EVAL - && !vim9script -#endif - ) - { - ea.cmdidx = CMD_print; - ea.argt = EX_RANGE+EX_COUNT+EX_TRLBAR; - if ((errormsg = invalid_range(&ea)) == NULL) - { - correct_range(&ea); - ex_print(&ea); - } - } - else if (ea.addr_count != 0) - { - if (ea.line2 > curbuf->b_ml.ml_line_count) - { - // With '-' in 'cpoptions' a line number past the file is an - // error, otherwise put it at the end of the file. - if (vim_strchr(p_cpo, CPO_MINUS) != NULL) - ea.line2 = -1; - else - ea.line2 = curbuf->b_ml.ml_line_count; - } - - if (ea.line2 < 0) - errormsg = _(e_invalid_range); - else - { - if (ea.line2 == 0) - curwin->w_cursor.lnum = 1; - else - curwin->w_cursor.lnum = ea.line2; - beginline(BL_SOL | BL_FIX); - } - } + errormsg = ex_range_without_command(&ea); goto doend; } @@ -2708,6 +2672,55 @@ ex_errmsg(char *msg, char_u *arg) } /* + * Handle a range without a command. + * Returns an error message on failure. + */ + char * +ex_range_without_command(exarg_T *eap) +{ + char *errormsg = NULL; + + if ((*eap->cmd == '|' || (exmode_active && eap->line1 != eap->line2)) +#ifdef FEAT_EVAL + && !in_vim9script() +#endif + ) + { + eap->cmdidx = CMD_print; + eap->argt = EX_RANGE+EX_COUNT+EX_TRLBAR; + if ((errormsg = invalid_range(eap)) == NULL) + { + correct_range(eap); + ex_print(eap); + } + } + else if (eap->addr_count != 0) + { + if (eap->line2 > curbuf->b_ml.ml_line_count) + { + // With '-' in 'cpoptions' a line number past the file is an + // error, otherwise put it at the end of the file. + if (vim_strchr(p_cpo, CPO_MINUS) != NULL) + eap->line2 = -1; + else + eap->line2 = curbuf->b_ml.ml_line_count; + } + + if (eap->line2 < 0) + errormsg = _(e_invalid_range); + else + { + if (eap->line2 == 0) + curwin->w_cursor.lnum = 1; + else + curwin->w_cursor.lnum = eap->line2; + beginline(BL_SOL | BL_FIX); + } + } + return errormsg; +} + +/* * Check for an Ex command with optional tail. * If there is a match advance "pp" to the argument and return TRUE. * If "noparen" is TRUE do not recognize the command followed by "(". diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index 53714391a7..d88b8a3e58 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -7,6 +7,7 @@ int getline_equal(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *co void *getline_cookie(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); char_u *getline_peek(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); char *ex_errmsg(char *msg, char_u *arg); +char *ex_range_without_command(exarg_T *eap); int checkforcmd(char_u **pp, char *cmd, int len); int checkforcmd_noparen(char_u **pp, char *cmd, int len); int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, int skip_only); @@ -22,7 +23,7 @@ int cmd_exists(char_u *name); void f_fullcommand(typval_T *argvars, typval_T *rettv); cmdidx_T excmd_get_cmdidx(char_u *cmd, int len); long excmd_get_argt(cmdidx_T idx); -char_u *skip_range(char_u *cmd, int skip_star, int *ctx); +char_u *skip_range(char_u *cmd_start, int skip_star, int *ctx); void ex_ni(exarg_T *eap); int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp); void separate_nextcmd(exarg_T *eap); diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index c90f54c936..1c6b4bb3a2 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -1999,6 +1999,25 @@ def Test_disassemble_execute() res) enddef +def s:OnlyRange() + :$ + :123 + :'m +enddef + +def Test_disassemble_range_only() + var res = execute('disass s:OnlyRange') + assert_match('\<SNR>\d*_OnlyRange\_s*' .. + ':$\_s*' .. + '\d EXECRANGE $\_s*' .. + ':123\_s*' .. + '\d EXECRANGE 123\_s*' .. + ':''m\_s*' .. + '\d EXECRANGE ''m\_s*' .. + '\d\+ RETURN void', + res) +enddef + def s:Echomsg() echomsg 'some' 'message' echoconsole 'nothing' diff --git a/src/version.c b/src/version.c index b98d0b3ca9..5724eeccd4 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3716, +/**/ 3715, /**/ 3714, diff --git a/src/vim9.h b/src/vim9.h index 88c0469d07..c82ba81d02 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -15,6 +15,7 @@ typedef enum { ISN_EXEC, // execute Ex command line isn_arg.string ISN_EXECCONCAT, // execute Ex command from isn_arg.number items on stack ISN_EXEC_SPLIT, // execute Ex command from isn_arg.string split at NL + ISN_EXECRANGE, // execute EX command that is only a range ISN_LEGACY_EVAL, // evaluate expression isn_arg.string with legacy syntax. ISN_ECHO, // :echo with isn_arg.echo.echo_count items on top of stack ISN_EXECUTE, // :execute with isn_arg.number items on top of stack diff --git a/src/vim9compile.c b/src/vim9compile.c index 2ed1f0e58e..a8e2c121d6 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2275,8 +2275,12 @@ generate_PUT(cctx_T *cctx, int regname, linenr_T lnum) return OK; } +/* + * Generate an EXEC instruction that takes a string argument. + * A copy is made of "line". + */ static int -generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *line) +generate_EXEC_copy(cctx_T *cctx, isntype_T isntype, char_u *line) { isn_T *isn; @@ -2287,6 +2291,29 @@ generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *line) return OK; } +/* + * Generate an EXEC instruction that takes a string argument. + * "str" must be allocated, it is consumed. + */ + static int +generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *str) +{ + isn_T *isn; + + if (cctx->ctx_skip == SKIP_YES) + { + vim_free(str); + return OK; + } + if ((isn = generate_instr(cctx, isntype)) == NULL) + { + vim_free(str); + return FAIL; + } + isn->isn_arg.string = str; + return OK; +} + static int generate_LEGACY_EVAL(cctx_T *cctx, char_u *line) { @@ -7552,7 +7579,7 @@ compile_lock_unlock( vim_snprintf((char *)buf, len, "%s %s", eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar", p); - ret = generate_EXEC(cctx, isn, buf); + ret = generate_EXEC_copy(cctx, isn, buf); vim_free(buf); *name_end = cc; @@ -9248,7 +9275,7 @@ compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx) generate_EXECCONCAT(cctx, count); } else - generate_EXEC(cctx, ISN_EXEC, line); + generate_EXEC_copy(cctx, ISN_EXEC, line); theend: if (*nextcmd != NUL) @@ -9874,10 +9901,12 @@ compile_def_function( if (ends_excmd2(line, ea.cmd)) { // A range without a command: jump to the line. - // TODO: compile to a more efficient command, possibly - // calling parse_cmd_address(). - ea.cmdidx = CMD_SIZE; - line = compile_exec(line, &ea, &cctx); + line = skipwhite(line); + while (*line == ':') + ++line; + generate_EXEC(&cctx, ISN_EXECRANGE, + vim_strnsave(line, ea.cmd - line)); + line = ea.cmd; goto nextline; } } @@ -10350,6 +10379,7 @@ delete_instr(isn_T *isn) { case ISN_DEF: case ISN_EXEC: + case ISN_EXECRANGE: case ISN_EXEC_SPLIT: case ISN_LEGACY_EVAL: case ISN_LOADAUTO: diff --git a/src/vim9execute.c b/src/vim9execute.c index de4de16e31..4b2763b1f9 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1774,6 +1774,28 @@ exec_instructions(ectx_T *ectx) } break; + // execute Ex command line that is only a range + case ISN_EXECRANGE: + { + exarg_T ea; + char *error = NULL; + + CLEAR_FIELD(ea); + ea.cmdidx = CMD_SIZE; + ea.addr_type = ADDR_LINES; + ea.cmd = iptr->isn_arg.string; + parse_cmd_address(&ea, &error, FALSE); + if (error == NULL) + error = ex_range_without_command(&ea); + if (error != NULL) + { + SOURCING_LNUM = iptr->isn_lnum; + emsg(error); + goto on_error; + } + } + break; + // Evaluate an expression with legacy syntax, push it onto the // stack. case ISN_LEGACY_EVAL: @@ -5068,6 +5090,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc) case ISN_EXEC_SPLIT: smsg("%s%4d EXEC_SPLIT %s", pfx, current, iptr->isn_arg.string); break; + case ISN_EXECRANGE: + smsg("%s%4d EXECRANGE %s", pfx, current, iptr->isn_arg.string); + break; case ISN_LEGACY_EVAL: smsg("%s%4d EVAL legacy %s", pfx, current, iptr->isn_arg.string); |