To: vim_dev@googlegroups.com Subject: Patch 8.2.2834 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2834 Problem: Vim9: :cexpr does not work with local variables. Solution: Compile :cexpr. Files: src/vim9compile.c, src/vim9.h, src/vim9execute.c, src/quickfix.c, src/proto/quickfix.pro, src/testdir/test_quickfix.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.2833/src/vim9compile.c 2021-04-25 13:54:38.988836743 +0200 --- src/vim9compile.c 2021-05-05 21:22:00.713708961 +0200 *************** *** 8704,8709 **** --- 8704,8737 ---- 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,9267 **** --- 9290,9305 ---- 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,9607 **** --- 9640,9649 ---- 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,9619 **** --- 9656,9662 ---- case ISN_BLOBINDEX: case ISN_BLOBSLICE: case ISN_CATCH: + case ISN_CEXPR_AUCMD: case ISN_CHECKLEN: case ISN_CHECKNR: case ISN_CMDMOD_REV: *** ../vim-8.2.2833/src/vim9.h 2021-04-26 20:32:54.918399234 +0200 --- src/vim9.h 2021-05-05 21:08:23.319561182 +0200 *************** *** 172,177 **** --- 172,180 ---- 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,357 **** --- 355,372 ---- 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,400 **** --- 410,416 ---- unpack_T unpack; isn_outer_T outer; subs_T subs; + cexpr_T cexpr; } isn_arg; }; *** ../vim-8.2.2833/src/vim9execute.c 2021-04-26 21:34:43.708519638 +0200 --- src/vim9execute.c 2021-05-05 21:19:31.250055070 +0200 *************** *** 1442,1447 **** --- 1442,1470 ---- } 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,4396 **** --- 4414,4433 ---- 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; *** ../vim-8.2.2833/src/quickfix.c 2021-05-03 18:57:02.392070898 +0200 --- src/quickfix.c 2021-05-05 21:16:02.518534404 +0200 *************** *** 7864,7870 **** /* * Return the autocmd name for the :cexpr Ex commands. */ ! static char_u * cexpr_get_auname(cmdidx_T cmdidx) { switch (cmdidx) --- 7864,7870 ---- /* * Return the autocmd name for the :cexpr Ex commands. */ ! char_u * cexpr_get_auname(cmdidx_T cmdidx) { switch (cmdidx) *************** *** 7879,7910 **** } } ! /* ! * ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command. ! * ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command. ! */ ! void ! ex_cexpr(exarg_T *eap) { ! typval_T *tv; ! qf_info_T *qi; ! char_u *au_name = NULL; ! int res; ! int_u save_qfid; ! win_T *wp = NULL; - 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 } qi = qf_cmd_get_or_alloc_stack(eap, &wp); if (qi == NULL) return; // Evaluate the expression. When the result is a string or a list we can --- 7879,7961 ---- } } ! int ! trigger_cexpr_autocmd(int cmdidx) { ! char_u *au_name = cexpr_get_auname(cmdidx); if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name, curbuf->b_fname, TRUE, curbuf)) { if (aborting()) ! 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,7953 **** 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: free_tv(tv); } } --- 7963,7969 ---- tv = eval_expr(eap->arg, eap); if (tv != NULL) { ! (void)cexpr_core(eap, tv); free_tv(tv); } } *** ../vim-8.2.2833/src/proto/quickfix.pro 2020-07-20 21:31:01.268823457 +0200 --- src/proto/quickfix.pro 2021-05-05 21:16:06.846524534 +0200 *************** *** 30,35 **** --- 30,38 ---- 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); *** ../vim-8.2.2833/src/testdir/test_quickfix.vim 2021-04-26 21:14:12.713924760 +0200 --- src/testdir/test_quickfix.vim 2021-05-05 21:25:42.969191409 +0200 *************** *** 722,727 **** --- 722,743 ---- 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! *** ../vim-8.2.2833/src/testdir/test_vim9_disassemble.vim 2021-04-20 22:16:35.355248931 +0200 --- src/testdir/test_vim9_disassemble.vim 2021-05-05 21:29:49.304542273 +0200 *************** *** 167,172 **** --- 167,191 ---- 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('\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 *** ../vim-8.2.2833/src/version.c 2021-05-05 19:58:12.921605195 +0200 --- src/version.c 2021-05-05 21:30:47.388251203 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2834, /**/ -- The term "free software" is defined by Richard M. Stallman as being software that isn't necessarily for free. Confusing? Let's call it "Stallman software" then! -- Bram Moolenaar /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///