changeset 26370:a3a0885d9dd8 v8.2.3716

patch 8.2.3716: Vim9: range without a command is not compiled Commit: https://github.com/vim/vim/commit/e4eed8c6db693a9183b776032570ce2f89dcffb6 Author: Bram Moolenaar <Bram@vim.org> Date: Wed Dec 1 15:22:56 2021 +0000 patch 8.2.3716: Vim9: range without a command is not compiled Problem: Vim9: range without a command is not compiled. Solution: Add the ISN_EXECRANGE byte code.
author Bram Moolenaar <Bram@vim.org>
date Wed, 01 Dec 2021 16:30:04 +0100
parents da56c576e583
children 7f4ef8856810
files src/ex_docmd.c src/proto/ex_docmd.pro src/testdir/test_vim9_disassemble.vim src/version.c src/vim9.h src/vim9compile.c src/vim9execute.c
diffstat 7 files changed, 137 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- 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 "(".
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -7,6 +7,7 @@ int getline_equal(char_u *(*fgetline)(in
 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);
--- 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'
--- 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,
--- 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
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2275,8 +2275,12 @@ generate_PUT(cctx_T *cctx, int regname, 
     return OK;
 }
 
-    static int
-generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *line)
+/*
+ * Generate an EXEC instruction that takes a string argument.
+ * A copy is made of "line".
+ */
+    static int
+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 is
     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 *
 	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:
--- 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 *inst
 	    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);