]> granicus.if.org Git - vim/commitdiff
patch 8.2.2834: Vim9: :cexpr does not work with local variables v8.2.2834
authorBram Moolenaar <Bram@vim.org>
Wed, 5 May 2021 19:31:39 +0000 (21:31 +0200)
committerBram Moolenaar <Bram@vim.org>
Wed, 5 May 2021 19:31:39 +0000 (21:31 +0200)
Problem:    Vim9: :cexpr does not work with local variables.
Solution:   Compile :cexpr.

src/proto/quickfix.pro
src/quickfix.c
src/testdir/test_quickfix.vim
src/testdir/test_vim9_disassemble.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index abe5b390c97ad96cf5af77d2626648897fe3e5e2..904a60a8ddb9bec0a57d0f687b970151f718a38a 100644 (file)
@@ -30,6 +30,9 @@ void ex_vimgrep(exarg_T *eap);
 int set_errorlist(win_T *wp, list_T *list, int action, char_u *title, dict_T *what);
 int set_ref_in_quickfix(int copyID);
 void ex_cbuffer(exarg_T *eap);
+char_u *cexpr_get_auname(cmdidx_T cmdidx);
+int trigger_cexpr_autocmd(int cmdidx);
+int cexpr_core(exarg_T *eap, typval_T *tv);
 void ex_cexpr(exarg_T *eap);
 void ex_helpgrep(exarg_T *eap);
 void f_getloclist(typval_T *argvars, typval_T *rettv);
index 9237b4b8146fe6448ed6c8d2a770f67e842f8a5f..665641c0ffd4621bd6891eca8490cd9b9df7f980 100644 (file)
@@ -7864,7 +7864,7 @@ ex_cbuffer(exarg_T *eap)
 /*
  * Return the autocmd name for the :cexpr Ex commands.
  */
-    static char_u *
+    char_u *
 cexpr_get_auname(cmdidx_T cmdidx)
 {
     switch (cmdidx)
@@ -7879,32 +7879,83 @@ cexpr_get_auname(cmdidx_T cmdidx)
     }
 }
 
-/*
- * ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command.
- * ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command.
- */
-    void
-ex_cexpr(exarg_T *eap)
+    int
+trigger_cexpr_autocmd(int cmdidx)
 {
-    typval_T   *tv;
-    qf_info_T  *qi;
-    char_u     *au_name = NULL;
-    int                res;
-    int_u      save_qfid;
-    win_T      *wp = NULL;
+    char_u     *au_name = cexpr_get_auname(cmdidx);
 
-    au_name = cexpr_get_auname(eap->cmdidx);
     if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
                                               curbuf->b_fname, TRUE, curbuf))
     {
-#ifdef FEAT_EVAL
        if (aborting())
-           return;
-#endif
+           return FAIL;
     }
+    return OK;
+}
+
+    int
+cexpr_core(exarg_T *eap, typval_T *tv)
+{
+    qf_info_T  *qi;
+    win_T      *wp = NULL;
 
     qi = qf_cmd_get_or_alloc_stack(eap, &wp);
     if (qi == NULL)
+       return FAIL;
+
+    if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL)
+           || (tv->v_type == VAR_LIST && tv->vval.v_list != NULL))
+    {
+       int     res;
+       int_u   save_qfid;
+       char_u  *au_name = cexpr_get_auname(eap->cmdidx);
+
+       incr_quickfix_busy();
+       res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, tv, p_efm,
+                       (eap->cmdidx != CMD_caddexpr
+                        && eap->cmdidx != CMD_laddexpr),
+                            (linenr_T)0, (linenr_T)0,
+                            qf_cmdtitle(*eap->cmdlinep), NULL);
+       if (qf_stack_empty(qi))
+       {
+           decr_quickfix_busy();
+           return FAIL;
+       }
+       if (res >= 0)
+           qf_list_changed(qf_get_curlist(qi));
+
+       // Remember the current quickfix list identifier, so that we can
+       // check for autocommands changing the current quickfix list.
+       save_qfid = qf_get_curlist(qi)->qf_id;
+       if (au_name != NULL)
+           apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
+                                           curbuf->b_fname, TRUE, curbuf);
+
+       // Jump to the first error for a new list and if autocmds didn't
+       // free the list.
+       if (res > 0 && (eap->cmdidx == CMD_cexpr || eap->cmdidx == CMD_lexpr)
+               && qflist_valid(wp, save_qfid))
+           // display the first error
+           qf_jump_first(qi, save_qfid, eap->forceit);
+       decr_quickfix_busy();
+       return OK;
+    }
+
+    emsg(_("E777: String or List expected"));
+    return FAIL;
+}
+
+/*
+ * ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command.
+ * ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command.
+ * Also: ":caddexpr", ":cgetexpr", "laddexpr" and "laddexpr".
+ */
+    void
+ex_cexpr(exarg_T *eap)
+{
+    typval_T   *tv;
+
+    if (trigger_cexpr_autocmd(eap->cmdidx) == FAIL)
        return;
 
     // Evaluate the expression.  When the result is a string or a list we can
@@ -7912,42 +7963,7 @@ ex_cexpr(exarg_T *eap)
     tv = eval_expr(eap->arg, eap);
     if (tv != NULL)
     {
-       if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL)
-               || (tv->v_type == VAR_LIST && tv->vval.v_list != NULL))
-       {
-           incr_quickfix_busy();
-           res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, tv, p_efm,
-                           (eap->cmdidx != CMD_caddexpr
-                            && eap->cmdidx != CMD_laddexpr),
-                                (linenr_T)0, (linenr_T)0,
-                                qf_cmdtitle(*eap->cmdlinep), NULL);
-           if (qf_stack_empty(qi))
-           {
-               decr_quickfix_busy();
-               goto cleanup;
-           }
-           if (res >= 0)
-               qf_list_changed(qf_get_curlist(qi));
-
-           // Remember the current quickfix list identifier, so that we can
-           // check for autocommands changing the current quickfix list.
-           save_qfid = qf_get_curlist(qi)->qf_id;
-           if (au_name != NULL)
-               apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name,
-                                               curbuf->b_fname, TRUE, curbuf);
-
-           // Jump to the first error for a new list and if autocmds didn't
-           // free the list.
-           if (res > 0 && (eap->cmdidx == CMD_cexpr
-                                                  || eap->cmdidx == CMD_lexpr)
-                   && qflist_valid(wp, save_qfid))
-               // display the first error
-               qf_jump_first(qi, save_qfid, eap->forceit);
-           decr_quickfix_busy();
-       }
-       else
-           emsg(_("E777: String or List expected"));
-cleanup:
+       (void)cexpr_core(eap, tv);
        free_tv(tv);
     }
 }
index 4c5ede5734535583f73d1cecdfdb3227768454cc..18b774c8b94e525b142b235099d20ef195bf3efe 100644 (file)
@@ -722,6 +722,22 @@ def Test_helpgrep_vim9_restore_cpo()
   helpclose
 enddef
 
+def Test_vim9_cexpr()
+  var text = 'somefile:95:error'
+  cexpr text
+  var l = getqflist()
+  assert_equal(1, l->len())
+  assert_equal(95, l[0].lnum)
+  assert_equal('error', l[0].text)
+
+  text = 'somefile:77:warning'
+  caddexpr text
+  l = getqflist()
+  assert_equal(2, l->len())
+  assert_equal(77, l[1].lnum)
+  assert_equal('warning', l[1].text)
+enddef
+
 func Test_errortitle()
   augroup QfBufWinEnter
     au!
index b1c1c18326e187c21d72fb2a84168a2743d17e98..e7314e43f37ae7f2469aceb872cfc2485ae40b58 100644 (file)
@@ -167,6 +167,25 @@ def Test_disassemble_redir_var()
         res)
 enddef
 
+def s:Cexpr()
+  var errors = "list of errors"
+  cexpr errors
+enddef
+
+def Test_disassemble_cexpr()
+  var res = execute('disass s:Cexpr')
+  assert_match('<SNR>\d*_Cexpr.*' ..
+        ' var errors = "list of errors"\_s*' ..
+        '\d PUSHS "list of errors"\_s*' ..
+        '\d STORE $0\_s*' ..
+        ' cexpr errors\_s*' ..
+        '\d CEXPR pre cexpr\_s*' ..
+        '\d LOAD $0\_s*' ..
+        '\d CEXPR core cexpr "cexpr errors"\_s*' ..
+        '\d RETURN 0',
+        res)
+enddef
+
 def s:YankRange()
   norm! m[jjm]
   :'[,']yank
index 93365768adf3ce3bebe00ecc3e545d71a84254e7..101036f803be1216b92275dddc21f870a0cb7653 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2834,
 /**/
     2833,
 /**/
index 5dc0a8f8da67586834644fee6eaa9e789013cbc2..94cc243ddcae50d7322726fa64fa6091e73667a1 100644 (file)
@@ -172,6 +172,9 @@ typedef enum {
     ISN_REDIRSTART, // :redir =>
     ISN_REDIREND,   // :redir END, isn_arg.number == 1 for append
 
+    ISN_CEXPR_AUCMD, // first part of :cexpr  isn_arg.number is cmdidx
+    ISN_CEXPR_CORE,  // second part of :cexpr, uses isn_arg.cexpr
+
     ISN_FINISH     // end marker in list of instructions
 } isntype_T;
 
@@ -352,6 +355,18 @@ typedef struct {
     isn_T      *subs_instr;    // sequence of instructions
 } subs_T;
 
+// indirect arguments to ISN_TRY
+typedef struct {
+    int                cer_cmdidx;
+    char_u     *cer_cmdline;
+    int                cer_forceit;
+} cexprref_T;
+
+// arguments to ISN_CEXPR_CORE
+typedef struct {
+    cexprref_T *cexpr_ref;
+} cexpr_T;
+
 /*
  * Instruction
  */
@@ -395,6 +410,7 @@ struct isn_S {
        unpack_T            unpack;
        isn_outer_T         outer;
        subs_T              subs;
+       cexpr_T             cexpr;
     } isn_arg;
 };
 
index 8589cec6009428775fd8a9772b93b83329b1672a..607f651d9064d3452a5669b5f246df79fa3d4480 100644 (file)
@@ -8704,6 +8704,34 @@ compile_redir(char_u *line, exarg_T *eap, cctx_T *cctx)
     return compile_exec(line, eap, cctx);
 }
 
+    static char_u *
+compile_cexpr(char_u *line, exarg_T *eap, cctx_T *cctx)
+{
+    isn_T      *isn;
+    char_u     *p;
+
+    isn = generate_instr(cctx, ISN_CEXPR_AUCMD);
+    if (isn == NULL)
+       return NULL;
+    isn->isn_arg.number = eap->cmdidx;
+
+    p = eap->arg;
+    if (compile_expr0(&p, cctx) == FAIL)
+       return NULL;
+
+    isn = generate_instr(cctx, ISN_CEXPR_CORE);
+    if (isn == NULL)
+       return NULL;
+    isn->isn_arg.cexpr.cexpr_ref = ALLOC_ONE(cexprref_T);
+    if (isn->isn_arg.cexpr.cexpr_ref == NULL)
+       return NULL;
+    isn->isn_arg.cexpr.cexpr_ref->cer_cmdidx = eap->cmdidx;
+    isn->isn_arg.cexpr.cexpr_ref->cer_forceit = eap->forceit;
+    isn->isn_arg.cexpr.cexpr_ref->cer_cmdline = vim_strsave(skipwhite(line));
+
+    return p;
+}
+
 /*
  * Add a function to the list of :def functions.
  * This sets "ufunc->uf_dfunc_idx" but the function isn't compiled yet.
@@ -9262,6 +9290,16 @@ compile_def_function(
                    line = compile_redir(line, &ea, &cctx);
                    break;
 
+           case CMD_cexpr:
+           case CMD_lexpr:
+           case CMD_caddexpr:
+           case CMD_laddexpr:
+           case CMD_cgetexpr:
+           case CMD_lgetexpr:
+                   ea.arg = p;
+                   line = compile_cexpr(line, &ea, &cctx);
+                   break;
+
            // TODO: any other commands with an expression argument?
 
            case CMD_append:
@@ -9602,6 +9640,10 @@ delete_instr(isn_T *isn)
            vim_free(isn->isn_arg.try.try_ref);
            break;
 
+       case ISN_CEXPR_CORE:
+           vim_free(isn->isn_arg.cexpr.cexpr_ref);
+           break;
+
        case ISN_2BOOL:
        case ISN_2STRING:
        case ISN_2STRING_ANY:
@@ -9614,6 +9656,7 @@ delete_instr(isn_T *isn)
        case ISN_BLOBINDEX:
        case ISN_BLOBSLICE:
        case ISN_CATCH:
+       case ISN_CEXPR_AUCMD:
        case ISN_CHECKLEN:
        case ISN_CHECKNR:
        case ISN_CMDMOD_REV:
index f46e3f36e99468ed3efb9849e71f044d684d7676..f260e6062cb02e05672e3bf73d36c930021e9167 100644 (file)
@@ -1442,6 +1442,29 @@ exec_instructions(ectx_T *ectx)
                }
                break;
 
+           case ISN_CEXPR_AUCMD:
+               if (trigger_cexpr_autocmd(iptr->isn_arg.number) == FAIL)
+                   goto on_error;
+               break;
+
+           case ISN_CEXPR_CORE:
+               {
+                   exarg_T ea;
+                   int     res;
+
+                   CLEAR_FIELD(ea);
+                   ea.cmdidx = iptr->isn_arg.cexpr.cexpr_ref->cer_cmdidx;
+                   ea.forceit = iptr->isn_arg.cexpr.cexpr_ref->cer_forceit;
+                   ea.cmdlinep = &iptr->isn_arg.cexpr.cexpr_ref->cer_cmdline;
+                   --ectx->ec_stack.ga_len;
+                   tv = STACK_TV_BOT(0);
+                   res = cexpr_core(&ea, tv);
+                   clear_tv(tv);
+                   if (res == FAIL)
+                       goto on_error;
+               }
+               break;
+
            // execute Ex command from pieces on the stack
            case ISN_EXECCONCAT:
                {
@@ -4391,6 +4414,20 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                smsg("%s%4d REDIR END%s", pfx, current,
                                        iptr->isn_arg.number ? " append" : "");
                break;
+           case ISN_CEXPR_AUCMD:
+               smsg("%s%4d CEXPR pre %s", pfx, current,
+                                      cexpr_get_auname(iptr->isn_arg.number));
+               break;
+           case ISN_CEXPR_CORE:
+               {
+                   cexprref_T      *cer = iptr->isn_arg.cexpr.cexpr_ref;
+
+                   smsg("%s%4d CEXPR core %s%s \"%s\"", pfx, current,
+                                      cexpr_get_auname(cer->cer_cmdidx),
+                                      cer->cer_forceit ? "!" : "",
+                                      cer->cer_cmdline);
+               }
+               break;
            case ISN_SUBSTITUTE:
                {
                    subs_T *subs = &iptr->isn_arg.subs;