]> granicus.if.org Git - vim/commitdiff
patch 8.2.3716: Vim9: range without a command is not compiled v8.2.3716
authorBram Moolenaar <Bram@vim.org>
Wed, 1 Dec 2021 15:22:56 +0000 (15:22 +0000)
committerBram Moolenaar <Bram@vim.org>
Wed, 1 Dec 2021 15:22:56 +0000 (15:22 +0000)
Problem:    Vim9: range without a command is not compiled.
Solution:   Add the ISN_EXECRANGE byte code.

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

index d4863115d85e27c8b811c1be71ab9597f9a521cc..439a78cedc5d07f689dd572f57289c0007c91de2 100644 (file)
@@ -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;
     }
 
@@ -2707,6 +2671,55 @@ ex_errmsg(char *msg, char_u *arg)
     return ex_error_buf;
 }
 
+/*
+ * 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.
index 53714391a777d4acfc7e70a51daaeef8b1275c9e..d88b8a3e58108f6d2c10e4cf9a5a07d8101b2ef6 100644 (file)
@@ -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);
index c90f54c9363233b9f95185fb12708569f346d256..1c6b4bb3a2b8108601c6834054c5956cdb1d11d5 100644 (file)
@@ -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'
index b98d0b3ca9ab4c6347d61727e93c5c338cbabcf4..5724eeccd4e7ab0bad0b1f7754e45d0312795cb9 100644 (file)
@@ -753,6 +753,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    3716,
 /**/
     3715,
 /**/
index 88c0469d07d4bda1146f42696769b29e8f5c929a..c82ba81d022ff298743c12b2dda2b4e1f79be3ce 100644 (file)
@@ -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
index 2ed1f0e58e19229281cee1703200ea5826b62c64..a8e2c121d6cc83261662314e5da2507ceabe0e33 100644 (file)
@@ -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:
index de4de16e31905eca664775cb0c821ede88281c3f..4b2763b1f9106ef162858b2eb69d12659671437f 100644 (file)
@@ -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);