mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.2.2785: Vim9: cannot redirect to local variable
Problem: Vim9: cannot redirect to local variable. Solution: Compile :redir when redirecting to a variable.
This commit is contained in:
parent
4c13721482
commit
2d1c57ed3d
@ -405,3 +405,7 @@ EXTERN char e_cannot_use_range_with_assignment_operator_str[]
|
|||||||
INIT(= N_("E1183: Cannot use a range with an assignment operator: %s"));
|
INIT(= N_("E1183: Cannot use a range with an assignment operator: %s"));
|
||||||
EXTERN char e_blob_not_set[]
|
EXTERN char e_blob_not_set[]
|
||||||
INIT(= N_("E1184: Blob not set"));
|
INIT(= N_("E1184: Blob not set"));
|
||||||
|
EXTERN char e_cannot_nest_redir[]
|
||||||
|
INIT(= N_("E1185: Cannot nest :redir"));
|
||||||
|
EXTERN char e_missing_redir_end[]
|
||||||
|
INIT(= N_("E1185: Missing :redir END"));
|
||||||
|
@ -3778,6 +3778,27 @@ static garray_T redir_ga; // only valid when redir_lval is not NULL
|
|||||||
static char_u *redir_endp = NULL;
|
static char_u *redir_endp = NULL;
|
||||||
static char_u *redir_varname = NULL;
|
static char_u *redir_varname = NULL;
|
||||||
|
|
||||||
|
int
|
||||||
|
alloc_redir_lval(void)
|
||||||
|
{
|
||||||
|
redir_lval = ALLOC_CLEAR_ONE(lval_T);
|
||||||
|
if (redir_lval == NULL)
|
||||||
|
return FAIL;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
clear_redir_lval(void)
|
||||||
|
{
|
||||||
|
VIM_CLEAR(redir_lval);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init_redir_ga(void)
|
||||||
|
{
|
||||||
|
ga_init2(&redir_ga, (int)sizeof(char), 500);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Start recording command output to a variable
|
* Start recording command output to a variable
|
||||||
* When "append" is TRUE append to an existing variable.
|
* When "append" is TRUE append to an existing variable.
|
||||||
@ -3801,15 +3822,14 @@ var_redir_start(char_u *name, int append)
|
|||||||
if (redir_varname == NULL)
|
if (redir_varname == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
redir_lval = ALLOC_CLEAR_ONE(lval_T);
|
if (alloc_redir_lval() == FAIL)
|
||||||
if (redir_lval == NULL)
|
|
||||||
{
|
{
|
||||||
var_redir_stop();
|
var_redir_stop();
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The output is stored in growarray "redir_ga" until redirection ends.
|
// The output is stored in growarray "redir_ga" until redirection ends.
|
||||||
ga_init2(&redir_ga, (int)sizeof(char), 500);
|
init_redir_ga();
|
||||||
|
|
||||||
// Parse the variable name (can be a dict or list entry).
|
// Parse the variable name (can be a dict or list entry).
|
||||||
redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0,
|
redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0,
|
||||||
@ -3921,6 +3941,20 @@ var_redir_stop(void)
|
|||||||
VIM_CLEAR(redir_varname);
|
VIM_CLEAR(redir_varname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the collected redirected text and clear redir_ga.
|
||||||
|
*/
|
||||||
|
char_u *
|
||||||
|
get_clear_redir_ga(void)
|
||||||
|
{
|
||||||
|
char_u *res;
|
||||||
|
|
||||||
|
ga_append(&redir_ga, NUL); // Append the trailing NUL.
|
||||||
|
res = redir_ga.ga_data;
|
||||||
|
redir_ga.ga_data = NULL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "gettabvar()" function
|
* "gettabvar()" function
|
||||||
*/
|
*/
|
||||||
|
@ -71,7 +71,7 @@ void vars_clear(hashtab_T *ht);
|
|||||||
void vars_clear_ext(hashtab_T *ht, int free_val);
|
void vars_clear_ext(hashtab_T *ht, int free_val);
|
||||||
void delete_var(hashtab_T *ht, hashitem_T *hi);
|
void delete_var(hashtab_T *ht, hashitem_T *hi);
|
||||||
void set_var(char_u *name, typval_T *tv, int copy);
|
void set_var(char_u *name, typval_T *tv, int copy);
|
||||||
void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, int flags, int var_idx);
|
void set_var_const(char_u *name, type_T *type, typval_T *tv_arg, int copy, int flags_arg, int var_idx);
|
||||||
int var_check_permission(dictitem_T *di, char_u *name);
|
int var_check_permission(dictitem_T *di, char_u *name);
|
||||||
int var_check_ro(int flags, char_u *name, int use_gettext);
|
int var_check_ro(int flags, char_u *name, int use_gettext);
|
||||||
int var_check_lock(int flags, char_u *name, int use_gettext);
|
int var_check_lock(int flags, char_u *name, int use_gettext);
|
||||||
@ -82,9 +82,13 @@ int valid_varname(char_u *varname, int autoload);
|
|||||||
void reset_v_option_vars(void);
|
void reset_v_option_vars(void);
|
||||||
void assert_error(garray_T *gap);
|
void assert_error(garray_T *gap);
|
||||||
int var_exists(char_u *var);
|
int var_exists(char_u *var);
|
||||||
|
int alloc_redir_lval(void);
|
||||||
|
void clear_redir_lval(void);
|
||||||
|
void init_redir_ga(void);
|
||||||
int var_redir_start(char_u *name, int append);
|
int var_redir_start(char_u *name, int append);
|
||||||
void var_redir_str(char_u *value, int value_len);
|
void var_redir_str(char_u *value, int value_len);
|
||||||
void var_redir_stop(void);
|
void var_redir_stop(void);
|
||||||
|
char_u *get_clear_redir_ga(void);
|
||||||
void f_gettabvar(typval_T *argvars, typval_T *rettv);
|
void f_gettabvar(typval_T *argvars, typval_T *rettv);
|
||||||
void f_gettabwinvar(typval_T *argvars, typval_T *rettv);
|
void f_gettabwinvar(typval_T *argvars, typval_T *rettv);
|
||||||
void f_getwinvar(typval_T *argvars, typval_T *rettv);
|
void f_getwinvar(typval_T *argvars, typval_T *rettv);
|
||||||
|
@ -1194,5 +1194,23 @@ def Test_substitute_expr()
|
|||||||
bwipe!
|
bwipe!
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_redir_to_var()
|
||||||
|
var result: string
|
||||||
|
redir => result
|
||||||
|
echo 'something'
|
||||||
|
redir END
|
||||||
|
assert_equal("\nsomething", result)
|
||||||
|
|
||||||
|
redir =>> result
|
||||||
|
echo 'more'
|
||||||
|
redir END
|
||||||
|
assert_equal("\nsomething\nmore", result)
|
||||||
|
|
||||||
|
var lines =<< trim END
|
||||||
|
redir => notexist
|
||||||
|
END
|
||||||
|
CheckDefFailure(lines, 'E1089:')
|
||||||
|
enddef
|
||||||
|
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@ -140,6 +140,33 @@ def Test_disassemble_substitute()
|
|||||||
res)
|
res)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def s:RedirVar()
|
||||||
|
var result: string
|
||||||
|
redir =>> result
|
||||||
|
echo "text"
|
||||||
|
redir END
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_disassemble_redir_var()
|
||||||
|
var res = execute('disass s:RedirVar')
|
||||||
|
assert_match('<SNR>\d*_RedirVar.*' ..
|
||||||
|
' var result: string\_s*' ..
|
||||||
|
'\d PUSHS "\[NULL\]"\_s*' ..
|
||||||
|
'\d STORE $0\_s*' ..
|
||||||
|
' redir =>> result\_s*' ..
|
||||||
|
'\d REDIR\_s*' ..
|
||||||
|
' echo "text"\_s*' ..
|
||||||
|
'\d PUSHS "text"\_s*' ..
|
||||||
|
'\d ECHO 1\_s*' ..
|
||||||
|
' redir END\_s*' ..
|
||||||
|
'\d LOAD $0\_s*' ..
|
||||||
|
'\d REDIR END\_s*' ..
|
||||||
|
'\d CONCAT\_s*' ..
|
||||||
|
'\d STORE $0\_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 */
|
||||||
|
/**/
|
||||||
|
2785,
|
||||||
/**/
|
/**/
|
||||||
2784,
|
2784,
|
||||||
/**/
|
/**/
|
||||||
|
@ -169,6 +169,9 @@ typedef enum {
|
|||||||
ISN_SHUFFLE, // move item on stack up or down
|
ISN_SHUFFLE, // move item on stack up or down
|
||||||
ISN_DROP, // pop stack and discard value
|
ISN_DROP, // pop stack and discard value
|
||||||
|
|
||||||
|
ISN_REDIRSTART, // :redir =>
|
||||||
|
ISN_REDIREND, // :redir END, isn_arg.number == 1 for append
|
||||||
|
|
||||||
ISN_FINISH // end marker in list of instructions
|
ISN_FINISH // end marker in list of instructions
|
||||||
} isntype_T;
|
} isntype_T;
|
||||||
|
|
||||||
|
@ -114,6 +114,52 @@ typedef struct {
|
|||||||
int lv_arg; // when TRUE this is an argument
|
int lv_arg; // when TRUE this is an argument
|
||||||
} lvar_T;
|
} lvar_T;
|
||||||
|
|
||||||
|
// Destination for an assignment or ":unlet" with an index.
|
||||||
|
typedef enum {
|
||||||
|
dest_local,
|
||||||
|
dest_option,
|
||||||
|
dest_env,
|
||||||
|
dest_global,
|
||||||
|
dest_buffer,
|
||||||
|
dest_window,
|
||||||
|
dest_tab,
|
||||||
|
dest_vimvar,
|
||||||
|
dest_script,
|
||||||
|
dest_reg,
|
||||||
|
dest_expr,
|
||||||
|
} assign_dest_T;
|
||||||
|
|
||||||
|
// Used by compile_lhs() to store information about the LHS of an assignment
|
||||||
|
// and one argument of ":unlet" with an index.
|
||||||
|
typedef struct {
|
||||||
|
assign_dest_T lhs_dest; // type of destination
|
||||||
|
|
||||||
|
char_u *lhs_name; // allocated name including
|
||||||
|
// "[expr]" or ".name".
|
||||||
|
size_t lhs_varlen; // length of the variable without
|
||||||
|
// "[expr]" or ".name"
|
||||||
|
char_u *lhs_dest_end; // end of the destination, including
|
||||||
|
// "[expr]" or ".name".
|
||||||
|
|
||||||
|
int lhs_has_index; // has "[expr]" or ".name"
|
||||||
|
|
||||||
|
int lhs_new_local; // create new local variable
|
||||||
|
int lhs_opt_flags; // for when destination is an option
|
||||||
|
int lhs_vimvaridx; // for when destination is a v:var
|
||||||
|
|
||||||
|
lvar_T lhs_local_lvar; // used for existing local destination
|
||||||
|
lvar_T lhs_arg_lvar; // used for argument destination
|
||||||
|
lvar_T *lhs_lvar; // points to destination lvar
|
||||||
|
int lhs_scriptvar_sid;
|
||||||
|
int lhs_scriptvar_idx;
|
||||||
|
|
||||||
|
int lhs_has_type; // type was specified
|
||||||
|
type_T *lhs_type;
|
||||||
|
type_T *lhs_member_type;
|
||||||
|
|
||||||
|
int lhs_append; // used by ISN_REDIREND
|
||||||
|
} lhs_T;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Context for compiling lines of Vim script.
|
* Context for compiling lines of Vim script.
|
||||||
* Stores info about the local variables and condition stack.
|
* Stores info about the local variables and condition stack.
|
||||||
@ -146,6 +192,9 @@ struct cctx_S {
|
|||||||
garray_T *ctx_type_list; // list of pointers to allocated types
|
garray_T *ctx_type_list; // list of pointers to allocated types
|
||||||
|
|
||||||
int ctx_has_cmdmod; // ISN_CMDMOD was generated
|
int ctx_has_cmdmod; // ISN_CMDMOD was generated
|
||||||
|
|
||||||
|
lhs_T ctx_redir_lhs; // LHS for ":redir => var", valid when
|
||||||
|
// lhs_name is not NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static void delete_def_function_contents(dfunc_T *dfunc, int mark_deleted);
|
static void delete_def_function_contents(dfunc_T *dfunc, int mark_deleted);
|
||||||
@ -5460,50 +5509,6 @@ static char *reserved[] = {
|
|||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
// Destination for an assignment or ":unlet" with an index.
|
|
||||||
typedef enum {
|
|
||||||
dest_local,
|
|
||||||
dest_option,
|
|
||||||
dest_env,
|
|
||||||
dest_global,
|
|
||||||
dest_buffer,
|
|
||||||
dest_window,
|
|
||||||
dest_tab,
|
|
||||||
dest_vimvar,
|
|
||||||
dest_script,
|
|
||||||
dest_reg,
|
|
||||||
dest_expr,
|
|
||||||
} assign_dest_T;
|
|
||||||
|
|
||||||
// Used by compile_lhs() to store information about the LHS of an assignment
|
|
||||||
// and one argument of ":unlet" with an index.
|
|
||||||
typedef struct {
|
|
||||||
assign_dest_T lhs_dest; // type of destination
|
|
||||||
|
|
||||||
char_u *lhs_name; // allocated name including
|
|
||||||
// "[expr]" or ".name".
|
|
||||||
size_t lhs_varlen; // length of the variable without
|
|
||||||
// "[expr]" or ".name"
|
|
||||||
char_u *lhs_dest_end; // end of the destination, including
|
|
||||||
// "[expr]" or ".name".
|
|
||||||
|
|
||||||
int lhs_has_index; // has "[expr]" or ".name"
|
|
||||||
|
|
||||||
int lhs_new_local; // create new local variable
|
|
||||||
int lhs_opt_flags; // for when destination is an option
|
|
||||||
int lhs_vimvaridx; // for when destination is a v:var
|
|
||||||
|
|
||||||
lvar_T lhs_local_lvar; // used for existing local destination
|
|
||||||
lvar_T lhs_arg_lvar; // used for argument destination
|
|
||||||
lvar_T *lhs_lvar; // points to destination lvar
|
|
||||||
int lhs_scriptvar_sid;
|
|
||||||
int lhs_scriptvar_idx;
|
|
||||||
|
|
||||||
int lhs_has_type; // type was specified
|
|
||||||
type_T *lhs_type;
|
|
||||||
type_T *lhs_member_type;
|
|
||||||
} lhs_T;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate the load instruction for "name".
|
* Generate the load instruction for "name".
|
||||||
*/
|
*/
|
||||||
@ -5778,6 +5783,44 @@ generate_store_var(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count)
|
||||||
|
{
|
||||||
|
if (lhs->lhs_dest != dest_local)
|
||||||
|
return generate_store_var(cctx, lhs->lhs_dest,
|
||||||
|
lhs->lhs_opt_flags, lhs->lhs_vimvaridx,
|
||||||
|
lhs->lhs_scriptvar_idx, lhs->lhs_scriptvar_sid,
|
||||||
|
lhs->lhs_type, lhs->lhs_name);
|
||||||
|
|
||||||
|
if (lhs->lhs_lvar != NULL)
|
||||||
|
{
|
||||||
|
garray_T *instr = &cctx->ctx_instr;
|
||||||
|
isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
|
||||||
|
|
||||||
|
// optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE into
|
||||||
|
// ISN_STORENR
|
||||||
|
if (lhs->lhs_lvar->lv_from_outer == 0
|
||||||
|
&& instr->ga_len == instr_count + 1
|
||||||
|
&& isn->isn_type == ISN_PUSHNR)
|
||||||
|
{
|
||||||
|
varnumber_T val = isn->isn_arg.number;
|
||||||
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
|
|
||||||
|
isn->isn_type = ISN_STORENR;
|
||||||
|
isn->isn_arg.storenr.stnr_idx = lhs->lhs_lvar->lv_idx;
|
||||||
|
isn->isn_arg.storenr.stnr_val = val;
|
||||||
|
if (stack->ga_len > 0)
|
||||||
|
--stack->ga_len;
|
||||||
|
}
|
||||||
|
else if (lhs->lhs_lvar->lv_from_outer > 0)
|
||||||
|
generate_STOREOUTER(cctx, lhs->lhs_lvar->lv_idx,
|
||||||
|
lhs->lhs_lvar->lv_from_outer);
|
||||||
|
else
|
||||||
|
generate_STORE(cctx, ISN_STORE, lhs->lhs_lvar->lv_idx, NULL);
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
is_decl_command(int cmdidx)
|
is_decl_command(int cmdidx)
|
||||||
{
|
{
|
||||||
@ -6083,6 +6126,36 @@ compile_lhs(
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Figure out the LHS and check a few errors.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
compile_assign_lhs(
|
||||||
|
char_u *var_start,
|
||||||
|
lhs_T *lhs,
|
||||||
|
int cmdidx,
|
||||||
|
int is_decl,
|
||||||
|
int heredoc,
|
||||||
|
int oplen,
|
||||||
|
cctx_T *cctx)
|
||||||
|
{
|
||||||
|
if (compile_lhs(var_start, lhs, cmdidx, heredoc, oplen, cctx) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
if (!lhs->lhs_has_index && lhs->lhs_lvar == &lhs->lhs_arg_lvar)
|
||||||
|
{
|
||||||
|
semsg(_(e_cannot_assign_to_argument), lhs->lhs_name);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
if (!is_decl && lhs->lhs_lvar != NULL
|
||||||
|
&& lhs->lhs_lvar->lv_const && !lhs->lhs_has_index)
|
||||||
|
{
|
||||||
|
semsg(_(e_cannot_assign_to_constant), lhs->lhs_name);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For an assignment with an index, compile the "idx" in "var[idx]" or "key" in
|
* For an assignment with an index, compile the "idx" in "var[idx]" or "key" in
|
||||||
* "var.key".
|
* "var.key".
|
||||||
@ -6454,21 +6527,9 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
/*
|
/*
|
||||||
* Figure out the LHS type and other properties.
|
* Figure out the LHS type and other properties.
|
||||||
*/
|
*/
|
||||||
if (compile_lhs(var_start, &lhs, cmdidx, heredoc, oplen, cctx) == FAIL)
|
if (compile_assign_lhs(var_start, &lhs, cmdidx,
|
||||||
|
is_decl, heredoc, oplen, cctx) == FAIL)
|
||||||
goto theend;
|
goto theend;
|
||||||
|
|
||||||
if (!lhs.lhs_has_index && lhs.lhs_lvar == &lhs.lhs_arg_lvar)
|
|
||||||
{
|
|
||||||
semsg(_(e_cannot_assign_to_argument), lhs.lhs_name);
|
|
||||||
goto theend;
|
|
||||||
}
|
|
||||||
if (!is_decl && lhs.lhs_lvar != NULL
|
|
||||||
&& lhs.lhs_lvar->lv_const && !lhs.lhs_has_index)
|
|
||||||
{
|
|
||||||
semsg(_(e_cannot_assign_to_constant), lhs.lhs_name);
|
|
||||||
goto theend;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!heredoc)
|
if (!heredoc)
|
||||||
{
|
{
|
||||||
if (cctx->ctx_skip == SKIP_YES)
|
if (cctx->ctx_skip == SKIP_YES)
|
||||||
@ -6728,39 +6789,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
// also in legacy script.
|
// also in legacy script.
|
||||||
generate_SETTYPE(cctx, lhs.lhs_type);
|
generate_SETTYPE(cctx, lhs.lhs_type);
|
||||||
|
|
||||||
if (lhs.lhs_dest != dest_local)
|
if (generate_store_lhs(cctx, &lhs, instr_count) == FAIL)
|
||||||
{
|
goto theend;
|
||||||
if (generate_store_var(cctx, lhs.lhs_dest,
|
|
||||||
lhs.lhs_opt_flags, lhs.lhs_vimvaridx,
|
|
||||||
lhs.lhs_scriptvar_idx, lhs.lhs_scriptvar_sid,
|
|
||||||
lhs.lhs_type, lhs.lhs_name) == FAIL)
|
|
||||||
goto theend;
|
|
||||||
}
|
|
||||||
else if (lhs.lhs_lvar != NULL)
|
|
||||||
{
|
|
||||||
isn_T *isn = ((isn_T *)instr->ga_data)
|
|
||||||
+ instr->ga_len - 1;
|
|
||||||
|
|
||||||
// optimization: turn "var = 123" from ISN_PUSHNR +
|
|
||||||
// ISN_STORE into ISN_STORENR
|
|
||||||
if (lhs.lhs_lvar->lv_from_outer == 0
|
|
||||||
&& instr->ga_len == instr_count + 1
|
|
||||||
&& isn->isn_type == ISN_PUSHNR)
|
|
||||||
{
|
|
||||||
varnumber_T val = isn->isn_arg.number;
|
|
||||||
|
|
||||||
isn->isn_type = ISN_STORENR;
|
|
||||||
isn->isn_arg.storenr.stnr_idx = lhs.lhs_lvar->lv_idx;
|
|
||||||
isn->isn_arg.storenr.stnr_val = val;
|
|
||||||
if (stack->ga_len > 0)
|
|
||||||
--stack->ga_len;
|
|
||||||
}
|
|
||||||
else if (lhs.lhs_lvar->lv_from_outer > 0)
|
|
||||||
generate_STOREOUTER(cctx, lhs.lhs_lvar->lv_idx,
|
|
||||||
lhs.lhs_lvar->lv_from_outer);
|
|
||||||
else
|
|
||||||
generate_STORE(cctx, ISN_STORE, lhs.lhs_lvar->lv_idx, NULL);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (var_idx + 1 < var_count)
|
if (var_idx + 1 < var_count)
|
||||||
@ -8541,6 +8571,67 @@ compile_substitute(char_u *arg, exarg_T *eap, cctx_T *cctx)
|
|||||||
return compile_exec(arg, eap, cctx);
|
return compile_exec(arg, eap, cctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char_u *
|
||||||
|
compile_redir(char_u *line, exarg_T *eap, cctx_T *cctx)
|
||||||
|
{
|
||||||
|
char_u *arg = eap->arg;
|
||||||
|
|
||||||
|
if (cctx->ctx_redir_lhs.lhs_name != NULL)
|
||||||
|
{
|
||||||
|
if (STRNCMP(arg, "END", 3) == 0)
|
||||||
|
{
|
||||||
|
if (cctx->ctx_redir_lhs.lhs_append)
|
||||||
|
{
|
||||||
|
if (compile_load_lhs(&cctx->ctx_redir_lhs,
|
||||||
|
cctx->ctx_redir_lhs.lhs_name, NULL, cctx) == FAIL)
|
||||||
|
return NULL;
|
||||||
|
if (cctx->ctx_redir_lhs.lhs_has_index)
|
||||||
|
emsg("redir with index not implemented yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the redirected text and put it on the stack, then store it
|
||||||
|
// in the variable.
|
||||||
|
generate_instr_type(cctx, ISN_REDIREND, &t_string);
|
||||||
|
|
||||||
|
if (cctx->ctx_redir_lhs.lhs_append)
|
||||||
|
generate_instr_drop(cctx, ISN_CONCAT, 1);
|
||||||
|
|
||||||
|
if (generate_store_lhs(cctx, &cctx->ctx_redir_lhs, -1) == FAIL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
VIM_CLEAR(cctx->ctx_redir_lhs.lhs_name);
|
||||||
|
return arg + 3;
|
||||||
|
}
|
||||||
|
emsg(_(e_cannot_nest_redir));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg[0] == '=' && arg[1] == '>')
|
||||||
|
{
|
||||||
|
int append = FALSE;
|
||||||
|
|
||||||
|
// redirect to a variable is compiled
|
||||||
|
arg += 2;
|
||||||
|
if (*arg == '>')
|
||||||
|
{
|
||||||
|
++arg;
|
||||||
|
append = TRUE;
|
||||||
|
}
|
||||||
|
arg = skipwhite(arg);
|
||||||
|
|
||||||
|
if (compile_assign_lhs(arg, &cctx->ctx_redir_lhs, CMD_redir,
|
||||||
|
FALSE, FALSE, 1, cctx) == FAIL)
|
||||||
|
return NULL;
|
||||||
|
generate_instr(cctx, ISN_REDIRSTART);
|
||||||
|
cctx->ctx_redir_lhs.lhs_append = append;
|
||||||
|
|
||||||
|
return arg + cctx->ctx_redir_lhs.lhs_varlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// other redirects are handled like at script level
|
||||||
|
return compile_exec(line, eap, cctx);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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.
|
||||||
@ -9082,6 +9173,11 @@ compile_def_function(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CMD_redir:
|
||||||
|
ea.arg = p;
|
||||||
|
line = compile_redir(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:
|
||||||
@ -9217,6 +9313,16 @@ erret:
|
|||||||
emsg(_(e_compiling_def_function_failed));
|
emsg(_(e_compiling_def_function_failed));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cctx.ctx_redir_lhs.lhs_name != NULL)
|
||||||
|
{
|
||||||
|
if (ret == OK)
|
||||||
|
{
|
||||||
|
emsg(_(e_missing_redir_end));
|
||||||
|
ret = FAIL;
|
||||||
|
}
|
||||||
|
vim_free(cctx.ctx_redir_lhs.lhs_name);
|
||||||
|
}
|
||||||
|
|
||||||
current_sctx = save_current_sctx;
|
current_sctx = save_current_sctx;
|
||||||
estack_compiling = save_estack_compiling;
|
estack_compiling = save_estack_compiling;
|
||||||
if (do_estack_push)
|
if (do_estack_push)
|
||||||
@ -9463,6 +9569,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_NEWLIST:
|
case ISN_NEWLIST:
|
||||||
case ISN_OPANY:
|
case ISN_OPANY:
|
||||||
case ISN_OPFLOAT:
|
case ISN_OPFLOAT:
|
||||||
|
case ISN_FINISH:
|
||||||
case ISN_OPNR:
|
case ISN_OPNR:
|
||||||
case ISN_PCALL:
|
case ISN_PCALL:
|
||||||
case ISN_PCALL_END:
|
case ISN_PCALL_END:
|
||||||
@ -9473,15 +9580,17 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_PUSHNR:
|
case ISN_PUSHNR:
|
||||||
case ISN_PUSHSPEC:
|
case ISN_PUSHSPEC:
|
||||||
case ISN_PUT:
|
case ISN_PUT:
|
||||||
|
case ISN_REDIREND:
|
||||||
|
case ISN_REDIRSTART:
|
||||||
case ISN_RETURN:
|
case ISN_RETURN:
|
||||||
case ISN_RETURN_ZERO:
|
case ISN_RETURN_ZERO:
|
||||||
case ISN_SHUFFLE:
|
case ISN_SHUFFLE:
|
||||||
case ISN_SLICE:
|
case ISN_SLICE:
|
||||||
case ISN_STORE:
|
case ISN_STORE:
|
||||||
case ISN_STOREINDEX:
|
case ISN_STOREINDEX:
|
||||||
case ISN_STORERANGE:
|
|
||||||
case ISN_STORENR:
|
case ISN_STORENR:
|
||||||
case ISN_STOREOUTER:
|
case ISN_STOREOUTER:
|
||||||
|
case ISN_STORERANGE:
|
||||||
case ISN_STOREREG:
|
case ISN_STOREREG:
|
||||||
case ISN_STOREV:
|
case ISN_STOREV:
|
||||||
case ISN_STRINDEX:
|
case ISN_STRINDEX:
|
||||||
@ -9491,7 +9600,6 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_UNLETINDEX:
|
case ISN_UNLETINDEX:
|
||||||
case ISN_UNLETRANGE:
|
case ISN_UNLETRANGE:
|
||||||
case ISN_UNPACK:
|
case ISN_UNPACK:
|
||||||
case ISN_FINISH:
|
|
||||||
// nothing allocated
|
// nothing allocated
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1409,6 +1409,37 @@ exec_instructions(ectx_T *ectx)
|
|||||||
case ISN_FINISH:
|
case ISN_FINISH:
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
case ISN_REDIRSTART:
|
||||||
|
// create a dummy entry for var_redir_str()
|
||||||
|
if (alloc_redir_lval() == FAIL)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
// The output is stored in growarray "redir_ga" until
|
||||||
|
// redirection ends.
|
||||||
|
init_redir_ga();
|
||||||
|
redir_vname = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ISN_REDIREND:
|
||||||
|
{
|
||||||
|
char_u *res = get_clear_redir_ga();
|
||||||
|
|
||||||
|
// End redirection, put redirected text on the stack.
|
||||||
|
clear_redir_lval();
|
||||||
|
redir_vname = 0;
|
||||||
|
|
||||||
|
if (GA_GROW(&ectx->ec_stack, 1) == FAIL)
|
||||||
|
{
|
||||||
|
vim_free(res);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
tv = STACK_TV_BOT(0);
|
||||||
|
tv->v_type = VAR_STRING;
|
||||||
|
tv->vval.v_string = res;
|
||||||
|
++ectx->ec_stack.ga_len;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// execute Ex command from pieces on the stack
|
// execute Ex command from pieces on the stack
|
||||||
case ISN_EXECCONCAT:
|
case ISN_EXECCONCAT:
|
||||||
{
|
{
|
||||||
@ -4332,6 +4363,13 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
case ISN_EXEC:
|
case ISN_EXEC:
|
||||||
smsg("%s%4d EXEC %s", pfx, current, iptr->isn_arg.string);
|
smsg("%s%4d EXEC %s", pfx, current, iptr->isn_arg.string);
|
||||||
break;
|
break;
|
||||||
|
case ISN_REDIRSTART:
|
||||||
|
smsg("%s%4d REDIR", pfx, current);
|
||||||
|
break;
|
||||||
|
case ISN_REDIREND:
|
||||||
|
smsg("%s%4d REDIR END%s", pfx, current,
|
||||||
|
iptr->isn_arg.number ? " append" : "");
|
||||||
|
break;
|
||||||
case ISN_SUBSTITUTE:
|
case ISN_SUBSTITUTE:
|
||||||
{
|
{
|
||||||
subs_T *subs = &iptr->isn_arg.subs;
|
subs_T *subs = &iptr->isn_arg.subs;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user