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:
@@ -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);
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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!
|
||||||
|
@@ -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
|
||||||
|
@@ -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,
|
||||||
/**/
|
/**/
|
||||||
|
16
src/vim9.h
16
src/vim9.h
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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:
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user