0
0
mirror of https://github.com/vim/vim.git synced 2025-09-27 04:14:06 -04:00

patch 8.2.2834: Vim9: :cexpr does not work with local variables

Problem:    Vim9: :cexpr does not work with local variables.
Solution:   Compile :cexpr.
This commit is contained in:
Bram Moolenaar
2021-05-05 21:31:39 +02:00
parent 3a00659db7
commit 5f7d4c049e
8 changed files with 206 additions and 54 deletions

View 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_errorlist(win_T *wp, list_T *list, int action, char_u *title, dict_T *what);
int set_ref_in_quickfix(int copyID); int set_ref_in_quickfix(int copyID);
void ex_cbuffer(exarg_T *eap); 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_cexpr(exarg_T *eap);
void ex_helpgrep(exarg_T *eap); void ex_helpgrep(exarg_T *eap);
void f_getloclist(typval_T *argvars, typval_T *rettv); void f_getloclist(typval_T *argvars, typval_T *rettv);

View File

@@ -7864,7 +7864,7 @@ ex_cbuffer(exarg_T *eap)
/* /*
* Return the autocmd name for the :cexpr Ex commands. * Return the autocmd name for the :cexpr Ex commands.
*/ */
static char_u * char_u *
cexpr_get_auname(cmdidx_T cmdidx) cexpr_get_auname(cmdidx_T cmdidx)
{ {
switch (cmdidx) switch (cmdidx)
@@ -7879,42 +7879,37 @@ cexpr_get_auname(cmdidx_T cmdidx)
} }
} }
/* int
* ":cexpr {expr}", ":cgetexpr {expr}", ":caddexpr {expr}" command. trigger_cexpr_autocmd(int cmdidx)
* ":lexpr {expr}", ":lgetexpr {expr}", ":laddexpr {expr}" command.
*/
void
ex_cexpr(exarg_T *eap)
{ {
typval_T *tv; char_u *au_name = cexpr_get_auname(cmdidx);
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, if (au_name != NULL && apply_autocmds(EVENT_QUICKFIXCMDPRE, au_name,
curbuf->b_fname, TRUE, curbuf)) curbuf->b_fname, TRUE, curbuf))
{ {
#ifdef FEAT_EVAL
if (aborting()) if (aborting())
return; return FAIL;
#endif
} }
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); qi = qf_cmd_get_or_alloc_stack(eap, &wp);
if (qi == NULL) if (qi == NULL)
return; return FAIL;
// Evaluate the expression. When the result is a string or a list we can
// use it to fill the errorlist.
tv = eval_expr(eap->arg, eap);
if (tv != NULL)
{
if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL) if ((tv->v_type == VAR_STRING && tv->vval.v_string != NULL)
|| (tv->v_type == VAR_LIST && tv->vval.v_list != 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(); incr_quickfix_busy();
res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, tv, p_efm, res = qf_init_ext(qi, qi->qf_curlist, NULL, NULL, tv, p_efm,
(eap->cmdidx != CMD_caddexpr (eap->cmdidx != CMD_caddexpr
@@ -7924,7 +7919,7 @@ ex_cexpr(exarg_T *eap)
if (qf_stack_empty(qi)) if (qf_stack_empty(qi))
{ {
decr_quickfix_busy(); decr_quickfix_busy();
goto cleanup; return FAIL;
} }
if (res >= 0) if (res >= 0)
qf_list_changed(qf_get_curlist(qi)); qf_list_changed(qf_get_curlist(qi));
@@ -7938,16 +7933,37 @@ ex_cexpr(exarg_T *eap)
// Jump to the first error for a new list and if autocmds didn't // Jump to the first error for a new list and if autocmds didn't
// free the list. // free the list.
if (res > 0 && (eap->cmdidx == CMD_cexpr if (res > 0 && (eap->cmdidx == CMD_cexpr || eap->cmdidx == CMD_lexpr)
|| eap->cmdidx == CMD_lexpr)
&& qflist_valid(wp, save_qfid)) && qflist_valid(wp, save_qfid))
// display the first error // display the first error
qf_jump_first(qi, save_qfid, eap->forceit); qf_jump_first(qi, save_qfid, eap->forceit);
decr_quickfix_busy(); decr_quickfix_busy();
return OK;
} }
else
emsg(_("E777: String or List expected")); emsg(_("E777: String or List expected"));
cleanup: 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
// use it to fill the errorlist.
tv = eval_expr(eap->arg, eap);
if (tv != NULL)
{
(void)cexpr_core(eap, tv);
free_tv(tv); free_tv(tv);
} }
} }

View File

@@ -722,6 +722,22 @@ def Test_helpgrep_vim9_restore_cpo()
helpclose helpclose
enddef 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() func Test_errortitle()
augroup QfBufWinEnter augroup QfBufWinEnter
au! au!

View File

@@ -167,6 +167,25 @@ def Test_disassemble_redir_var()
res) res)
enddef 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() def s:YankRange()
norm! m[jjm] norm! m[jjm]
:'[,']yank :'[,']yank

View File

@@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
2834,
/**/ /**/
2833, 2833,
/**/ /**/

View File

@@ -172,6 +172,9 @@ typedef enum {
ISN_REDIRSTART, // :redir => ISN_REDIRSTART, // :redir =>
ISN_REDIREND, // :redir END, isn_arg.number == 1 for append 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 ISN_FINISH // end marker in list of instructions
} isntype_T; } isntype_T;
@@ -352,6 +355,18 @@ typedef struct {
isn_T *subs_instr; // sequence of instructions isn_T *subs_instr; // sequence of instructions
} subs_T; } 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 * Instruction
*/ */
@@ -395,6 +410,7 @@ struct isn_S {
unpack_T unpack; unpack_T unpack;
isn_outer_T outer; isn_outer_T outer;
subs_T subs; subs_T subs;
cexpr_T cexpr;
} isn_arg; } isn_arg;
}; };

View File

@@ -8704,6 +8704,34 @@ compile_redir(char_u *line, exarg_T *eap, cctx_T *cctx)
return compile_exec(line, eap, 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. * Add a function to the list of :def functions.
* This sets "ufunc->uf_dfunc_idx" but the function isn't compiled yet. * 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); line = compile_redir(line, &ea, &cctx);
break; 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? // TODO: any other commands with an expression argument?
case CMD_append: case CMD_append:
@@ -9602,6 +9640,10 @@ delete_instr(isn_T *isn)
vim_free(isn->isn_arg.try.try_ref); vim_free(isn->isn_arg.try.try_ref);
break; break;
case ISN_CEXPR_CORE:
vim_free(isn->isn_arg.cexpr.cexpr_ref);
break;
case ISN_2BOOL: case ISN_2BOOL:
case ISN_2STRING: case ISN_2STRING:
case ISN_2STRING_ANY: case ISN_2STRING_ANY:
@@ -9614,6 +9656,7 @@ delete_instr(isn_T *isn)
case ISN_BLOBINDEX: case ISN_BLOBINDEX:
case ISN_BLOBSLICE: case ISN_BLOBSLICE:
case ISN_CATCH: case ISN_CATCH:
case ISN_CEXPR_AUCMD:
case ISN_CHECKLEN: case ISN_CHECKLEN:
case ISN_CHECKNR: case ISN_CHECKNR:
case ISN_CMDMOD_REV: case ISN_CMDMOD_REV:

View File

@@ -1442,6 +1442,29 @@ exec_instructions(ectx_T *ectx)
} }
break; 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 // execute Ex command from pieces on the stack
case ISN_EXECCONCAT: 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, smsg("%s%4d REDIR END%s", pfx, current,
iptr->isn_arg.number ? " append" : ""); iptr->isn_arg.number ? " append" : "");
break; 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: case ISN_SUBSTITUTE:
{ {
subs_T *subs = &iptr->isn_arg.subs; subs_T *subs = &iptr->isn_arg.subs;