summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2021-12-01 15:22:56 +0000
committerBram Moolenaar <Bram@vim.org>2021-12-01 15:22:56 +0000
commite4eed8c6db693a9183b776032570ce2f89dcffb6 (patch)
tree8edb253b95ca4b3dcfa430b8c9b3c9151d6257d9
parentf0e496a85a89fb1fd21b6af6363dcfc276331110 (diff)
downloadvim-git-e4eed8c6db693a9183b776032570ce2f89dcffb6.tar.gz
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.c87
-rw-r--r--src/proto/ex_docmd.pro3
-rw-r--r--src/testdir/test_vim9_disassemble.vim19
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h1
-rw-r--r--src/vim9compile.c44
-rw-r--r--src/vim9execute.c25
7 files changed, 136 insertions, 45 deletions
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index d4863115d..439a78ced 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 53714391a..d88b8a3e5 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 c90f54c93..1c6b4bb3a 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 b98d0b3ca..5724eeccd 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 88c0469d0..c82ba81d0 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 2ed1f0e58..a8e2c121d 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 de4de16e3..4b2763b1f 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);