forked from aniani/vim
patch 8.2.3716: Vim9: range without a command is not compiled
Problem: Vim9: range without a command is not compiled. Solution: Add the ISN_EXECRANGE byte code.
This commit is contained in:
@@ -1977,43 +1977,7 @@ do_one_cmd(
|
||||
*/
|
||||
if (ea.skip) // skip this if inside :if
|
||||
goto doend;
|
||||
if ((*ea.cmd == '|' || (exmode_active && ea.line1 != ea.line2))
|
||||
#ifdef FEAT_EVAL
|
||||
&& !vim9script
|
||||
#endif
|
||||
)
|
||||
{
|
||||
ea.cmdidx = CMD_print;
|
||||
ea.argt = EX_RANGE+EX_COUNT+EX_TRLBAR;
|
||||
if ((errormsg = invalid_range(&ea)) == NULL)
|
||||
{
|
||||
correct_range(&ea);
|
||||
ex_print(&ea);
|
||||
}
|
||||
}
|
||||
else if (ea.addr_count != 0)
|
||||
{
|
||||
if (ea.line2 > curbuf->b_ml.ml_line_count)
|
||||
{
|
||||
// With '-' in 'cpoptions' a line number past the file is an
|
||||
// error, otherwise put it at the end of the file.
|
||||
if (vim_strchr(p_cpo, CPO_MINUS) != NULL)
|
||||
ea.line2 = -1;
|
||||
else
|
||||
ea.line2 = curbuf->b_ml.ml_line_count;
|
||||
}
|
||||
|
||||
if (ea.line2 < 0)
|
||||
errormsg = _(e_invalid_range);
|
||||
else
|
||||
{
|
||||
if (ea.line2 == 0)
|
||||
curwin->w_cursor.lnum = 1;
|
||||
else
|
||||
curwin->w_cursor.lnum = ea.line2;
|
||||
beginline(BL_SOL | BL_FIX);
|
||||
}
|
||||
}
|
||||
errormsg = ex_range_without_command(&ea);
|
||||
goto doend;
|
||||
}
|
||||
|
||||
@@ -2707,6 +2671,55 @@ ex_errmsg(char *msg, char_u *arg)
|
||||
return ex_error_buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a range without a command.
|
||||
* Returns an error message on failure.
|
||||
*/
|
||||
char *
|
||||
ex_range_without_command(exarg_T *eap)
|
||||
{
|
||||
char *errormsg = NULL;
|
||||
|
||||
if ((*eap->cmd == '|' || (exmode_active && eap->line1 != eap->line2))
|
||||
#ifdef FEAT_EVAL
|
||||
&& !in_vim9script()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
eap->cmdidx = CMD_print;
|
||||
eap->argt = EX_RANGE+EX_COUNT+EX_TRLBAR;
|
||||
if ((errormsg = invalid_range(eap)) == NULL)
|
||||
{
|
||||
correct_range(eap);
|
||||
ex_print(eap);
|
||||
}
|
||||
}
|
||||
else if (eap->addr_count != 0)
|
||||
{
|
||||
if (eap->line2 > curbuf->b_ml.ml_line_count)
|
||||
{
|
||||
// With '-' in 'cpoptions' a line number past the file is an
|
||||
// error, otherwise put it at the end of the file.
|
||||
if (vim_strchr(p_cpo, CPO_MINUS) != NULL)
|
||||
eap->line2 = -1;
|
||||
else
|
||||
eap->line2 = curbuf->b_ml.ml_line_count;
|
||||
}
|
||||
|
||||
if (eap->line2 < 0)
|
||||
errormsg = _(e_invalid_range);
|
||||
else
|
||||
{
|
||||
if (eap->line2 == 0)
|
||||
curwin->w_cursor.lnum = 1;
|
||||
else
|
||||
curwin->w_cursor.lnum = eap->line2;
|
||||
beginline(BL_SOL | BL_FIX);
|
||||
}
|
||||
}
|
||||
return errormsg;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for an Ex command with optional tail.
|
||||
* If there is a match advance "pp" to the argument and return TRUE.
|
||||
|
@@ -7,6 +7,7 @@ int getline_equal(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *co
|
||||
void *getline_cookie(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie);
|
||||
char_u *getline_peek(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie);
|
||||
char *ex_errmsg(char *msg, char_u *arg);
|
||||
char *ex_range_without_command(exarg_T *eap);
|
||||
int checkforcmd(char_u **pp, char *cmd, int len);
|
||||
int checkforcmd_noparen(char_u **pp, char *cmd, int len);
|
||||
int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, int skip_only);
|
||||
@@ -22,7 +23,7 @@ int cmd_exists(char_u *name);
|
||||
void f_fullcommand(typval_T *argvars, typval_T *rettv);
|
||||
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);
|
||||
long excmd_get_argt(cmdidx_T idx);
|
||||
char_u *skip_range(char_u *cmd, int skip_star, int *ctx);
|
||||
char_u *skip_range(char_u *cmd_start, int skip_star, int *ctx);
|
||||
void ex_ni(exarg_T *eap);
|
||||
int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp);
|
||||
void separate_nextcmd(exarg_T *eap);
|
||||
|
@@ -1999,6 +1999,25 @@ def Test_disassemble_execute()
|
||||
res)
|
||||
enddef
|
||||
|
||||
def s:OnlyRange()
|
||||
:$
|
||||
:123
|
||||
:'m
|
||||
enddef
|
||||
|
||||
def Test_disassemble_range_only()
|
||||
var res = execute('disass s:OnlyRange')
|
||||
assert_match('\<SNR>\d*_OnlyRange\_s*' ..
|
||||
':$\_s*' ..
|
||||
'\d EXECRANGE $\_s*' ..
|
||||
':123\_s*' ..
|
||||
'\d EXECRANGE 123\_s*' ..
|
||||
':''m\_s*' ..
|
||||
'\d EXECRANGE ''m\_s*' ..
|
||||
'\d\+ RETURN void',
|
||||
res)
|
||||
enddef
|
||||
|
||||
def s:Echomsg()
|
||||
echomsg 'some' 'message'
|
||||
echoconsole 'nothing'
|
||||
|
@@ -753,6 +753,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
3716,
|
||||
/**/
|
||||
3715,
|
||||
/**/
|
||||
|
@@ -15,6 +15,7 @@ typedef enum {
|
||||
ISN_EXEC, // execute Ex command line isn_arg.string
|
||||
ISN_EXECCONCAT, // execute Ex command from isn_arg.number items on stack
|
||||
ISN_EXEC_SPLIT, // execute Ex command from isn_arg.string split at NL
|
||||
ISN_EXECRANGE, // execute EX command that is only a range
|
||||
ISN_LEGACY_EVAL, // evaluate expression isn_arg.string with legacy syntax.
|
||||
ISN_ECHO, // :echo with isn_arg.echo.echo_count items on top of stack
|
||||
ISN_EXECUTE, // :execute with isn_arg.number items on top of stack
|
||||
|
@@ -2275,8 +2275,12 @@ generate_PUT(cctx_T *cctx, int regname, linenr_T lnum)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an EXEC instruction that takes a string argument.
|
||||
* A copy is made of "line".
|
||||
*/
|
||||
static int
|
||||
generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *line)
|
||||
generate_EXEC_copy(cctx_T *cctx, isntype_T isntype, char_u *line)
|
||||
{
|
||||
isn_T *isn;
|
||||
|
||||
@@ -2287,6 +2291,29 @@ generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *line)
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an EXEC instruction that takes a string argument.
|
||||
* "str" must be allocated, it is consumed.
|
||||
*/
|
||||
static int
|
||||
generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *str)
|
||||
{
|
||||
isn_T *isn;
|
||||
|
||||
if (cctx->ctx_skip == SKIP_YES)
|
||||
{
|
||||
vim_free(str);
|
||||
return OK;
|
||||
}
|
||||
if ((isn = generate_instr(cctx, isntype)) == NULL)
|
||||
{
|
||||
vim_free(str);
|
||||
return FAIL;
|
||||
}
|
||||
isn->isn_arg.string = str;
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int
|
||||
generate_LEGACY_EVAL(cctx_T *cctx, char_u *line)
|
||||
{
|
||||
@@ -7552,7 +7579,7 @@ compile_lock_unlock(
|
||||
vim_snprintf((char *)buf, len, "%s %s",
|
||||
eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar",
|
||||
p);
|
||||
ret = generate_EXEC(cctx, isn, buf);
|
||||
ret = generate_EXEC_copy(cctx, isn, buf);
|
||||
|
||||
vim_free(buf);
|
||||
*name_end = cc;
|
||||
@@ -9248,7 +9275,7 @@ compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx)
|
||||
generate_EXECCONCAT(cctx, count);
|
||||
}
|
||||
else
|
||||
generate_EXEC(cctx, ISN_EXEC, line);
|
||||
generate_EXEC_copy(cctx, ISN_EXEC, line);
|
||||
|
||||
theend:
|
||||
if (*nextcmd != NUL)
|
||||
@@ -9874,10 +9901,12 @@ compile_def_function(
|
||||
if (ends_excmd2(line, ea.cmd))
|
||||
{
|
||||
// A range without a command: jump to the line.
|
||||
// TODO: compile to a more efficient command, possibly
|
||||
// calling parse_cmd_address().
|
||||
ea.cmdidx = CMD_SIZE;
|
||||
line = compile_exec(line, &ea, &cctx);
|
||||
line = skipwhite(line);
|
||||
while (*line == ':')
|
||||
++line;
|
||||
generate_EXEC(&cctx, ISN_EXECRANGE,
|
||||
vim_strnsave(line, ea.cmd - line));
|
||||
line = ea.cmd;
|
||||
goto nextline;
|
||||
}
|
||||
}
|
||||
@@ -10350,6 +10379,7 @@ delete_instr(isn_T *isn)
|
||||
{
|
||||
case ISN_DEF:
|
||||
case ISN_EXEC:
|
||||
case ISN_EXECRANGE:
|
||||
case ISN_EXEC_SPLIT:
|
||||
case ISN_LEGACY_EVAL:
|
||||
case ISN_LOADAUTO:
|
||||
|
@@ -1774,6 +1774,28 @@ exec_instructions(ectx_T *ectx)
|
||||
}
|
||||
break;
|
||||
|
||||
// execute Ex command line that is only a range
|
||||
case ISN_EXECRANGE:
|
||||
{
|
||||
exarg_T ea;
|
||||
char *error = NULL;
|
||||
|
||||
CLEAR_FIELD(ea);
|
||||
ea.cmdidx = CMD_SIZE;
|
||||
ea.addr_type = ADDR_LINES;
|
||||
ea.cmd = iptr->isn_arg.string;
|
||||
parse_cmd_address(&ea, &error, FALSE);
|
||||
if (error == NULL)
|
||||
error = ex_range_without_command(&ea);
|
||||
if (error != NULL)
|
||||
{
|
||||
SOURCING_LNUM = iptr->isn_lnum;
|
||||
emsg(error);
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// Evaluate an expression with legacy syntax, push it onto the
|
||||
// stack.
|
||||
case ISN_LEGACY_EVAL:
|
||||
@@ -5068,6 +5090,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
||||
case ISN_EXEC_SPLIT:
|
||||
smsg("%s%4d EXEC_SPLIT %s", pfx, current, iptr->isn_arg.string);
|
||||
break;
|
||||
case ISN_EXECRANGE:
|
||||
smsg("%s%4d EXECRANGE %s", pfx, current, iptr->isn_arg.string);
|
||||
break;
|
||||
case ISN_LEGACY_EVAL:
|
||||
smsg("%s%4d EVAL legacy %s", pfx, current,
|
||||
iptr->isn_arg.string);
|
||||
|
Reference in New Issue
Block a user