Merge remote-tracking branch 'vim/master'

This commit is contained in:
ichizok
2020-08-28 14:38:45 +09:00
34 changed files with 699 additions and 208 deletions
+17 -9
View File
@@ -2848,7 +2848,8 @@ str2list({expr} [, {utf8}]) List convert each character of {expr} to
str2nr({expr} [, {base} [, {quoted}]])
Number convert String to Number
strcharpart({str}, {start} [, {len}])
String {len} characters of {str} at {start}
String {len} characters of {str} at
character {start}
strchars({expr} [, {skipcc}]) Number character length of the String {expr}
strdisplaywidth({expr} [, {col}]) Number display length of the String {expr}
strftime({format} [, {time}]) String format time with a specified format
@@ -2857,8 +2858,9 @@ stridx({haystack}, {needle} [, {start}])
Number index of {needle} in {haystack}
string({expr}) String String representation of {expr} value
strlen({expr}) Number length of the String {expr}
strpart({str}, {start} [, {len}])
String {len} bytes of {str} at byte {start}
strpart({str}, {start} [, {len} [, {chars}]])
String {len} bytes/chars of {str} at
byte {start}
strptime({format}, {timestring})
Number Convert {timestring} to unix timestamp
strridx({haystack}, {needle} [, {start}])
@@ -3430,7 +3432,8 @@ byte2line({byte}) *byte2line()*
byteidx({expr}, {nr}) *byteidx()*
Return byte index of the {nr}'th character in the string
{expr}. Use zero for the first character, it returns zero.
{expr}. Use zero for the first character, it then returns
zero.
This function is only useful when there are multibyte
characters, otherwise the returned value is equal to {nr}.
Composing characters are not counted separately, their byte
@@ -9960,17 +9963,22 @@ strlen({expr}) The result is a Number, which is the length of the String
{expr} in bytes.
If the argument is a Number it is first converted to a String.
For other types an error is given.
If you want to count the number of multi-byte characters use
If you want to count the number of multibyte characters use
|strchars()|.
Also see |len()|, |strdisplaywidth()| and |strwidth()|.
Can also be used as a |method|: >
GetString()->strlen()
strpart({src}, {start} [, {len}]) *strpart()*
strpart({src}, {start} [, {len} [, {chars}]]) *strpart()*
The result is a String, which is part of {src}, starting from
byte {start}, with the byte length {len}.
To count characters instead of bytes use |strcharpart()|.
When {chars} is present and TRUE then {len} is the number of
characters positions (composing characters are not counted
separately, thus "1" means one base character and any
following composing characters).
To count {start} as characters instead of bytes use
|strcharpart()|.
When bytes are selected which do not exist, this doesn't
result in an error, the bytes are simply omitted.
@@ -9982,8 +9990,8 @@ strpart({src}, {start} [, {len}]) *strpart()*
strpart("abcdefg", 3) == "defg"
< Note: To get the first character, {start} must be 0. For
example, to get three bytes under and after the cursor: >
strpart(getline("."), col(".") - 1, 3)
example, to get the character under the cursor: >
strpart(getline("."), col(".") - 1, 1, v:true)
<
Can also be used as a |method|: >
GetText()->strpart(5)
+1 -1
View File
@@ -923,7 +923,7 @@ channel_connect(
*waittime -= elapsed_msec;
if (waitnow > 0)
{
mch_delay((long)waitnow, TRUE);
mch_delay((long)waitnow, MCH_DELAY_IGNOREINPUT);
ui_breakcheck();
*waittime -= waitnow;
}
+6 -5
View File
@@ -2675,6 +2675,7 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
int oplen;
int concat;
typval_T var2;
int vim9script = in_vim9script();
// "." is only string concatenation when scriptversion is 1
p = eval_next_non_blank(*arg, evalarg, &getnext);
@@ -2689,7 +2690,7 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
*arg = eval_next_line(evalarg);
else
{
if (evaluate && in_vim9script() && !VIM_ISWHITE(**arg))
if (evaluate && vim9script && !VIM_ISWHITE(**arg))
{
error_white_both(p, oplen);
clear_tv(rettv);
@@ -2721,14 +2722,14 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
/*
* Get the second variable.
*/
if (evaluate && in_vim9script() && !IS_WHITE_OR_NUL((*arg)[oplen]))
if (evaluate && vim9script && !IS_WHITE_OR_NUL((*arg)[oplen]))
{
error_white_both(p, oplen);
clear_tv(rettv);
return FAIL;
}
*arg = skipwhite_and_linebreak(*arg + oplen, evalarg);
if (eval6(arg, &var2, evalarg, !in_vim9script() && op == '.') == FAIL)
if (eval6(arg, &var2, evalarg, !vim9script && op == '.') == FAIL)
{
clear_tv(rettv);
return FAIL;
@@ -2745,12 +2746,12 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
char_u *s1 = tv_get_string_buf(rettv, buf1);
char_u *s2 = NULL;
if (in_vim9script() && (var2.v_type == VAR_VOID
if (vim9script && (var2.v_type == VAR_VOID
|| var2.v_type == VAR_CHANNEL
|| var2.v_type == VAR_JOB))
emsg(_(e_inval_string));
#ifdef FEAT_FLOAT
else if (var2.v_type == VAR_FLOAT)
else if (vim9script && var2.v_type == VAR_FLOAT)
{
vim_snprintf((char *)buf2, NUMBUFLEN, "%g",
var2.vval.v_float);
+13 -5
View File
@@ -950,7 +950,7 @@ static funcentry_T global_functions[] =
{"stridx", 2, 3, FEARG_1, ret_number, f_stridx},
{"string", 1, 1, FEARG_1, ret_string, f_string},
{"strlen", 1, 1, FEARG_1, ret_number, f_strlen},
{"strpart", 2, 3, FEARG_1, ret_string, f_strpart},
{"strpart", 2, 4, FEARG_1, ret_string, f_strpart},
{"strptime", 2, 2, FEARG_1, ret_number,
#ifdef HAVE_STRPTIME
f_strptime
@@ -8305,10 +8305,8 @@ f_strpart(typval_T *argvars, typval_T *rettv)
else
len = slen - n; // default len: all bytes that are available.
/*
* Only return the overlap between the specified part and the actual
* string.
*/
// Only return the overlap between the specified part and the actual
// string.
if (n < 0)
{
len += n;
@@ -8321,6 +8319,16 @@ f_strpart(typval_T *argvars, typval_T *rettv)
else if (n + len > slen)
len = slen - n;
if (argvars[2].v_type != VAR_UNKNOWN && argvars[3].v_type != VAR_UNKNOWN)
{
int off;
// length in characters
for (off = n; off < slen && len > 0; --len)
off += mb_ptr2len(p + off);
len = off - n;
}
rettv->v_type = VAR_STRING;
rettv->vval.v_string = vim_strnsave(p + n, len);
}
+14
View File
@@ -2463,6 +2463,20 @@ eval_variable(
tv = sv->sv_tv;
}
}
else if (in_vim9script())
{
ufunc_T *ufunc = find_func(name, FALSE, NULL);
if (ufunc != NULL)
{
foundFunc = TRUE;
if (rettv != NULL)
{
rettv->v_type = VAR_FUNC;
rettv->vval.v_string = vim_strsave(ufunc->uf_name);
}
}
}
}
if (!foundFunc)
+146 -101
View File
@@ -66,7 +66,9 @@ static int getargopt(exarg_T *eap);
# define ex_cexpr ex_ni
#endif
static linenr_T default_address(exarg_T *eap);
static linenr_T get_address(exarg_T *, char_u **, cmd_addr_T addr_type, int skip, int silent, int to_other_file, int address_count);
static void address_default_all(exarg_T *eap);
static void get_flags(exarg_T *eap);
#if !defined(FEAT_PERL) \
|| !defined(FEAT_PYTHON) || !defined(FEAT_PYTHON3) \
@@ -1886,7 +1888,9 @@ do_one_cmd(
ea.cmd = cmd;
#ifdef FEAT_EVAL
if (may_have_range)
if (!may_have_range)
ea.line1 = ea.line2 = default_address(&ea);
else
#endif
if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
goto doend;
@@ -2288,59 +2292,7 @@ do_one_cmd(
}
if ((ea.argt & EX_DFLALL) && ea.addr_count == 0)
{
buf_T *buf;
ea.line1 = 1;
switch (ea.addr_type)
{
case ADDR_LINES:
case ADDR_OTHER:
ea.line2 = curbuf->b_ml.ml_line_count;
break;
case ADDR_LOADED_BUFFERS:
buf = firstbuf;
while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL)
buf = buf->b_next;
ea.line1 = buf->b_fnum;
buf = lastbuf;
while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL)
buf = buf->b_prev;
ea.line2 = buf->b_fnum;
break;
case ADDR_BUFFERS:
ea.line1 = firstbuf->b_fnum;
ea.line2 = lastbuf->b_fnum;
break;
case ADDR_WINDOWS:
ea.line2 = LAST_WIN_NR;
break;
case ADDR_TABS:
ea.line2 = LAST_TAB_NR;
break;
case ADDR_TABS_RELATIVE:
ea.line2 = 1;
break;
case ADDR_ARGUMENTS:
if (ARGCOUNT == 0)
ea.line1 = ea.line2 = 0;
else
ea.line2 = ARGCOUNT;
break;
case ADDR_QUICKFIX_VALID:
#ifdef FEAT_QUICKFIX
ea.line2 = qf_get_valid_size(&ea);
if (ea.line2 == 0)
ea.line2 = 1;
#endif
break;
case ADDR_NONE:
case ADDR_UNSIGNED:
case ADDR_QUICKFIX:
iemsg(_("INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"));
break;
}
}
address_default_all(&ea);
// accept numbered register only when no count allowed (:put)
if ( (ea.argt & EX_REGSTR)
@@ -3017,50 +2969,7 @@ parse_cmd_address(exarg_T *eap, char **errormsg, int silent)
for (;;)
{
eap->line1 = eap->line2;
switch (eap->addr_type)
{
case ADDR_LINES:
case ADDR_OTHER:
// Default is the cursor line number. Avoid using an invalid
// line number though.
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
eap->line2 = curbuf->b_ml.ml_line_count;
else
eap->line2 = curwin->w_cursor.lnum;
break;
case ADDR_WINDOWS:
eap->line2 = CURRENT_WIN_NR;
break;
case ADDR_ARGUMENTS:
eap->line2 = curwin->w_arg_idx + 1;
if (eap->line2 > ARGCOUNT)
eap->line2 = ARGCOUNT;
break;
case ADDR_LOADED_BUFFERS:
case ADDR_BUFFERS:
eap->line2 = curbuf->b_fnum;
break;
case ADDR_TABS:
eap->line2 = CURRENT_TAB_NR;
break;
case ADDR_TABS_RELATIVE:
case ADDR_UNSIGNED:
eap->line2 = 1;
break;
case ADDR_QUICKFIX:
#ifdef FEAT_QUICKFIX
eap->line2 = qf_get_cur_idx(eap);
#endif
break;
case ADDR_QUICKFIX_VALID:
#ifdef FEAT_QUICKFIX
eap->line2 = qf_get_cur_valid_idx(eap);
#endif
break;
case ADDR_NONE:
// Will give an error later if a range is found.
break;
}
eap->line2 = default_address(eap);
eap->cmd = skipwhite(eap->cmd);
lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent,
eap->addr_count == 0, address_count++);
@@ -3248,6 +3157,27 @@ append_command(char_u *cmd)
*d = NUL;
}
/*
* If "start" points "&opt", "&l:opt", "&g:opt" or "$ENV" return a pointer to
* the name. Otherwise just return "start".
*/
char_u *
skip_option_env_lead(char_u *start)
{
char_u *name = start;
if (*start == '&')
{
if ((start[1] == 'l' || start[1] == 'g') && start[2] == ':')
name += 3;
else
name += 1;
}
else if (*start == '$')
name += 1;
return name;
}
/*
* Find an Ex command by its name, either built-in or user.
* Start of the name can be found at eap->cmd.
@@ -3279,9 +3209,7 @@ find_ex_command(
p = eap->cmd;
if (lookup != NULL)
{
// Skip over first char for "&opt = val", "$ENV = val" and "@r = val".
char_u *pskip = (*eap->cmd == '&' || *eap->cmd == '$')
? eap->cmd + 1 : eap->cmd;
char_u *pskip = skip_option_env_lead(eap->cmd);
if (vim_strchr((char_u *)"{('[\"@", *p) != NULL
|| ((p = to_name_const_end(pskip)) > eap->cmd && *p != NUL))
@@ -3659,6 +3587,61 @@ addr_error(cmd_addr_T addr_type)
emsg(_(e_invrange));
}
/*
* Return the default address for an address type.
*/
static linenr_T
default_address(exarg_T *eap)
{
linenr_T lnum = 0;
switch (eap->addr_type)
{
case ADDR_LINES:
case ADDR_OTHER:
// Default is the cursor line number. Avoid using an invalid
// line number though.
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
lnum = curbuf->b_ml.ml_line_count;
else
lnum = curwin->w_cursor.lnum;
break;
case ADDR_WINDOWS:
lnum = CURRENT_WIN_NR;
break;
case ADDR_ARGUMENTS:
lnum = curwin->w_arg_idx + 1;
if (lnum > ARGCOUNT)
lnum = ARGCOUNT;
break;
case ADDR_LOADED_BUFFERS:
case ADDR_BUFFERS:
lnum = curbuf->b_fnum;
break;
case ADDR_TABS:
lnum = CURRENT_TAB_NR;
break;
case ADDR_TABS_RELATIVE:
case ADDR_UNSIGNED:
lnum = 1;
break;
case ADDR_QUICKFIX:
#ifdef FEAT_QUICKFIX
lnum = qf_get_cur_idx(eap);
#endif
break;
case ADDR_QUICKFIX_VALID:
#ifdef FEAT_QUICKFIX
lnum = qf_get_cur_valid_idx(eap);
#endif
break;
case ADDR_NONE:
// Will give an error later if a range is found.
break;
}
return lnum;
}
/*
* Get a single EX address.
*
@@ -4020,6 +4003,68 @@ error:
return lnum;
}
/*
* Set eap->line1 and eap->line2 to the whole range.
* Used for commands with the EX_DFLALL flag and no range given.
*/
static void
address_default_all(exarg_T *eap)
{
eap->line1 = 1;
switch (eap->addr_type)
{
case ADDR_LINES:
case ADDR_OTHER:
eap->line2 = curbuf->b_ml.ml_line_count;
break;
case ADDR_LOADED_BUFFERS:
{
buf_T *buf = firstbuf;
while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL)
buf = buf->b_next;
eap->line1 = buf->b_fnum;
buf = lastbuf;
while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL)
buf = buf->b_prev;
eap->line2 = buf->b_fnum;
}
break;
case ADDR_BUFFERS:
eap->line1 = firstbuf->b_fnum;
eap->line2 = lastbuf->b_fnum;
break;
case ADDR_WINDOWS:
eap->line2 = LAST_WIN_NR;
break;
case ADDR_TABS:
eap->line2 = LAST_TAB_NR;
break;
case ADDR_TABS_RELATIVE:
eap->line2 = 1;
break;
case ADDR_ARGUMENTS:
if (ARGCOUNT == 0)
eap->line1 = eap->line2 = 0;
else
eap->line2 = ARGCOUNT;
break;
case ADDR_QUICKFIX_VALID:
#ifdef FEAT_QUICKFIX
eap->line2 = qf_get_valid_size(eap);
if (eap->line2 == 0)
eap->line2 = 1;
#endif
break;
case ADDR_NONE:
case ADDR_UNSIGNED:
case ADDR_QUICKFIX:
iemsg(_("INTERNAL: Cannot use EX_DFLALL with ADDR_NONE, ADDR_UNSIGNED or ADDR_QUICKFIX"));
break;
}
}
/*
* Get flags from an Ex command argument.
*/
+2 -2
View File
@@ -2243,7 +2243,7 @@ cs_release_csp(int i, int freefnpp)
waitpid_errno = errno;
if (pid != 0)
break; // break unless the process is still running
mch_delay(50L, FALSE); // sleep 50 ms
mch_delay(50L, 0); // sleep 50 ms
}
# endif
/*
@@ -2278,7 +2278,7 @@ cs_release_csp(int i, int freefnpp)
alive = FALSE; // cscope process no longer exists
break;
}
mch_delay(50L, FALSE); // sleep 50ms
mch_delay(50L, 0); // sleep 50 ms
}
}
if (alive)
+3 -1
View File
@@ -3656,8 +3656,10 @@ nv_ident(cmdarg_T *cap)
{
if (g_cmd)
STRCPY(buf, "tj ");
else if (cap->count0 == 0)
STRCPY(buf, "ta ");
else
sprintf((char *)buf, "%ldta ", cap->count0);
sprintf((char *)buf, ":%ldta ", cap->count0);
}
}
+3 -3
View File
@@ -222,10 +222,10 @@ mch_avail_mem(int special)
/*
* Waits a specified amount of time, or until input arrives if
* ignoreinput is FALSE.
* flags does not have MCH_DELAY_IGNOREINPUT.
*/
void
mch_delay(long msec, int ignoreinput)
mch_delay(long msec, int flags)
{
#ifndef LATTICE // SAS declares void Delay(ULONG)
void Delay(long);
@@ -233,7 +233,7 @@ mch_delay(long msec, int ignoreinput)
if (msec > 0)
{
if (ignoreinput)
if (flags & MCH_DELAY_IGNOREINPUT)
Delay(msec / 20L); // Delay works with 20 msec intervals
else
WaitForChar(raw_in, msec * 1000L);
+22 -13
View File
@@ -577,15 +577,19 @@ mch_total_mem(int special UNUSED)
}
#endif
/*
* "flags": MCH_DELAY_IGNOREINPUT - don't read input
* MCH_DELAY_SETTMODE - use settmode() even for short delays
*/
void
mch_delay(long msec, int ignoreinput)
mch_delay(long msec, int flags)
{
tmode_T old_tmode;
#ifdef FEAT_MZSCHEME
long total = msec; // remember original value
#endif
if (ignoreinput)
if (flags & MCH_DELAY_IGNOREINPUT)
{
// Go to cooked mode without echo, to allow SIGINT interrupting us
// here. But we don't want QUIT to kill us (CTRL-\ used in a
@@ -593,7 +597,8 @@ mch_delay(long msec, int ignoreinput)
// Only do this if sleeping for more than half a second.
in_mch_delay = TRUE;
old_tmode = mch_cur_tmode;
if (mch_cur_tmode == TMODE_RAW && msec > 500)
if (mch_cur_tmode == TMODE_RAW
&& (msec > 500 || (flags & MCH_DELAY_SETTMODE)))
settmode(TMODE_SLEEP);
/*
@@ -636,10 +641,8 @@ mch_delay(long msec, int ignoreinput)
tv.tv_sec = msec / 1000;
tv.tv_usec = (msec % 1000) * 1000;
/*
* NOTE: Solaris 2.6 has a bug that makes select() hang here. Get
* a patch from Sun to fix this. Reported by Gunnar Pedersen.
*/
// NOTE: Solaris 2.6 has a bug that makes select() hang here. Get
// a patch from Sun to fix this. Reported by Gunnar Pedersen.
select(0, NULL, NULL, NULL, &tv);
}
# endif // HAVE_SELECT
@@ -650,7 +653,7 @@ mch_delay(long msec, int ignoreinput)
while (total > 0);
#endif
if (msec > 500)
if (msec > 500 || (flags & MCH_DELAY_SETTMODE))
settmode(old_tmode);
in_mch_delay = FALSE;
}
@@ -1284,7 +1287,7 @@ mch_suspend(void)
long wait_time;
for (wait_time = 0; !sigcont_received && wait_time <= 3L; wait_time++)
mch_delay(wait_time, FALSE);
mch_delay(wait_time, 0);
}
# endif
in_mch_suspend = FALSE;
@@ -4176,7 +4179,7 @@ wait4pid(pid_t child, waitstatus *status)
if (wait_pid == 0)
{
// Wait for 1 to 10 msec before trying again.
mch_delay(delay_msec, TRUE);
mch_delay(delay_msec, MCH_DELAY_IGNOREINPUT | MCH_DELAY_SETTMODE);
if (++delay_msec > 10)
delay_msec = 10;
continue;
@@ -5284,6 +5287,9 @@ finished:
{
long delay_msec = 1;
out_str(T_CTE); // possibly disables modifyOtherKeys, so that
// the system can recognize CTRL-C
/*
* Similar to the loop above, but only handle X events, no
* I/O.
@@ -5317,11 +5323,14 @@ finished:
clip_update();
// Wait for 1 to 10 msec. 1 is faster but gives the child
// less time.
mch_delay(delay_msec, TRUE);
// less time, gradually wait longer.
mch_delay(delay_msec,
MCH_DELAY_IGNOREINPUT | MCH_DELAY_SETTMODE);
if (++delay_msec > 10)
delay_msec = 10;
}
out_str(T_CTI); // possibly enables modifyOtherKeys again
}
# endif
@@ -6732,7 +6741,7 @@ mch_expand_wildcards(
// When running in the background, give it some time to create the temp
// file, but don't wait for it to finish.
if (ampersand)
mch_delay(10L, TRUE);
mch_delay(10L, MCH_DELAY_IGNOREINPUT);
extra_shell_arg = NULL; // cleanup
show_shell_mess = TRUE;
+2 -2
View File
@@ -6739,7 +6739,7 @@ notsgr:
void
mch_delay(
long msec,
int ignoreinput UNUSED)
int flags UNUSED)
{
#if defined(FEAT_GUI_MSWIN) && !defined(VIMDLL)
Sleep((int)msec); // never wait for input
@@ -6751,7 +6751,7 @@ mch_delay(
return;
}
# endif
if (ignoreinput)
if (flags & MCH_DELAY_IGNOREINPUT)
# ifdef FEAT_MZSCHEME
if (mzthreads_allowed() && p_mzq > 0 && msec > p_mzq)
{
+1
View File
@@ -10,6 +10,7 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only);
void undo_cmdmod(exarg_T *eap, int save_msg_scroll);
int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
int checkforcmd(char_u **pp, char *cmd, int len);
char_u *skip_option_env_lead(char_u *start);
char_u *find_ex_command(exarg_T *eap, int *full, void *(*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx);
int modifier_len(char_u *cmd);
int cmd_exists(char_u *name);
+1 -1
View File
@@ -5,7 +5,7 @@ void mch_write(char_u *p, int len);
int mch_inchar(char_u *buf, int maxlen, long time, int tb_change_cnt);
int mch_char_avail(void);
long_u mch_avail_mem(int special);
void mch_delay(long msec, int ignoreinput);
void mch_delay(long msec, int flags);
void mch_suspend(void);
void mch_init(void);
int mch_check_win(int argc, char **argv);
+1 -1
View File
@@ -5,7 +5,7 @@ int mch_inchar(char_u *buf, int maxlen, long wtime, int tb_change_cnt);
int mch_char_avail(void);
int mch_check_messages(void);
long_u mch_total_mem(int special);
void mch_delay(long msec, int ignoreinput);
void mch_delay(long msec, int flags);
int mch_stackcheck(char *p);
void mch_suspend(void);
void mch_init(void);
+1 -1
View File
@@ -53,7 +53,7 @@ int mch_signal_job(job_T *job, char_u *how);
void mch_clear_job(job_T *job);
void mch_set_normal_colors(void);
void mch_write(char_u *s, int len);
void mch_delay(long msec, int ignoreinput);
void mch_delay(long msec, int flags);
int mch_remove(char_u *name);
void mch_breakcheck(int force);
long_u mch_total_mem(int special);
+2 -1
View File
@@ -465,7 +465,8 @@ screen_line(
// double-wide character. Clear the left half to avoid it getting the popup
// window background color.
if (coloff > 0 && ScreenLines[off_to] == 0
&& ScreenLinesUC[off_to - 1] != 0)
&& ScreenLinesUC[off_to - 1] != 0
&& (*mb_char2cells)(ScreenLinesUC[off_to - 1]) > 1)
{
ScreenLines[off_to - 1] = ' ';
ScreenLinesUC[off_to - 1] = 0;
+1
View File
@@ -66,6 +66,7 @@ struct slang_S
int sl_add; // TRUE if it's a .add file.
char_u *sl_fbyts; // case-folded word bytes
long sl_fbyts_len; // length of sl_fbyts
idx_T *sl_fidxs; // case-folded word indexes
char_u *sl_kbyts; // keep-case word bytes
idx_T *sl_kidxs; // keep-case word indexes
+11 -7
View File
@@ -315,7 +315,7 @@ static int read_compound(FILE *fd, slang_T *slang, int len);
static int set_sofo(slang_T *lp, char_u *from, char_u *to);
static void set_sal_first(slang_T *lp);
static int *mb_str2wide(char_u *s);
static int spell_read_tree(FILE *fd, char_u **bytsp, idx_T **idxsp, int prefixtree, int prefixcnt);
static int spell_read_tree(FILE *fd, char_u **bytsp, long *bytsp_len, idx_T **idxsp, int prefixtree, int prefixcnt);
static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx_T startidx, int prefixtree, int maxprefcondnr);
static void set_spell_charflags(char_u *flags, int cnt, char_u *upp);
static int set_spell_chartab(char_u *fol, char_u *low, char_u *upp);
@@ -553,17 +553,18 @@ truncerr:
}
// <LWORDTREE>
res = spell_read_tree(fd, &lp->sl_fbyts, &lp->sl_fidxs, FALSE, 0);
res = spell_read_tree(fd, &lp->sl_fbyts, &lp->sl_fbyts_len,
&lp->sl_fidxs, FALSE, 0);
if (res != 0)
goto someerror;
// <KWORDTREE>
res = spell_read_tree(fd, &lp->sl_kbyts, &lp->sl_kidxs, FALSE, 0);
res = spell_read_tree(fd, &lp->sl_kbyts, NULL, &lp->sl_kidxs, FALSE, 0);
if (res != 0)
goto someerror;
// <PREFIXTREE>
res = spell_read_tree(fd, &lp->sl_pbyts, &lp->sl_pidxs, TRUE,
res = spell_read_tree(fd, &lp->sl_pbyts, NULL, &lp->sl_pidxs, TRUE,
lp->sl_prefixcnt);
if (res != 0)
goto someerror;
@@ -737,7 +738,7 @@ suggest_load_files(void)
* <SUGWORDTREE>: <wordtree>
* Read the trie with the soundfolded words.
*/
if (spell_read_tree(fd, &slang->sl_sbyts, &slang->sl_sidxs,
if (spell_read_tree(fd, &slang->sl_sbyts, NULL, &slang->sl_sidxs,
FALSE, 0) != 0)
{
someerror:
@@ -1572,6 +1573,7 @@ mb_str2wide(char_u *s)
spell_read_tree(
FILE *fd,
char_u **bytsp,
long *bytsp_len,
idx_T **idxsp,
int prefixtree, // TRUE for the prefix tree
int prefixcnt) // when "prefixtree" is TRUE: prefix count
@@ -1596,6 +1598,8 @@ spell_read_tree(
if (bp == NULL)
return SP_OTHERERROR;
*bytsp = bp;
if (bytsp_len != NULL)
*bytsp_len = len;
// Allocate the index array.
ip = lalloc_clear(len * sizeof(int), TRUE);
@@ -5609,8 +5613,8 @@ sug_filltree(spellinfo_T *spin, slang_T *slang)
spin->si_blocks_cnt = 0;
// Skip over any other NUL bytes (same word with different
// flags).
while (byts[n + 1] == 0)
// flags). But don't go over the end.
while (n + 1 < slang->sl_fbyts_len && byts[n + 1] == 0)
{
++n;
++curi[depth];
+1 -1
View File
@@ -3604,7 +3604,7 @@ stoptermcap(void)
{
# ifdef UNIX
// Give the terminal a chance to respond.
mch_delay(100L, FALSE);
mch_delay(100L, 0);
# endif
# ifdef TCIFLUSH
// Discard data received but not read.
+1 -1
View File
@@ -117,7 +117,7 @@ $(TEST_OUTFILES): $(DOSTMP)\$(*B).in
# Limitation: Only works with the +eval feature.
newtests: newtestssilent
@if exist messages (findstr "SKIPPED FAILED" messages > nul) && type messages
@if exist messages type messages
newtestssilent: $(NEW_TESTS_RES)
+1 -1
View File
@@ -129,7 +129,7 @@ $(DOSTMP)/%.in : %.in
# Limitation: Only works with the +eval feature.
newtests: newtestssilent
@if exist messages (findstr "SKIPPED FAILED" messages > nul) && type messages
@if exist messages type messages
newtestssilent: $(NEW_TESTS_RES)
+1 -1
View File
@@ -126,7 +126,7 @@ tinytests: $(SCRIPTS_TINY_OUT)
RUN_VIMTEST = VIMRUNTIME=$(SCRIPTSOURCE) $(VALGRIND) $(VIMPROG) -f $(GUI_FLAG) -u unix.vim
newtests: newtestssilent
@/bin/sh -c "if test -f messages && grep -q 'SKIPPED\|FAILED' messages; then cat messages; fi"
@/bin/sh -c "if test -f messages; then cat messages; fi"
newtestssilent: $(NEW_TESTS_RES)
+6
View File
@@ -135,6 +135,12 @@ func Test_string_concatenation()
let a = 'a'
let a..=b
call assert_equal('ab', a)
if has('float')
let a = 'A'
let b = 1.234
call assert_fails('echo a .. b', 'E806:')
endif
endfunc
" Test fix for issue #4507
+4
View File
@@ -513,6 +513,10 @@ func Test_strpart()
call assert_equal('lép', strpart('éléphant', 2, 4))
call assert_equal('léphant', strpart('éléphant', 2))
call assert_equal('é', strpart('éléphant', 0, 1, 1))
call assert_equal('ép', strpart('éléphant', 3, 2, v:true))
call assert_equal('ó', strpart('cómposed', 1, 1, 1))
endfunc
func Test_tolower()
+241
View File
@@ -410,6 +410,24 @@ func Test_sugfile_format_error()
call assert_fails("let s = spellsuggest('abc')", 'E782:')
set nospell spelllang&
" invalid suggest word count in SUGTABLE
set encoding=utf-8
call writefile(0z56494D7375670100000000000000440000000022, sugfile)
set runtimepath=./Xtest
set spelllang=Xtest
set spell
call assert_fails("let s = spellsuggest('abc')", 'E782:')
set nospell spelllang&
" missing sugline in SUGTABLE
set encoding=utf-8
call writefile(0z56494D7375670100000000000000440000000000000005, sugfile)
set runtimepath=./Xtest
set spelllang=Xtest
set spell
call assert_fails("let s = spellsuggest('abc')", 'E782:')
set nospell spelllang&
let &rtp = save_rtp
call delete('Xtest', 'rf')
endfunc
@@ -510,6 +528,197 @@ func Test_mkspell()
call assert_fails('mkspell en en_US abc_xyz', 'E755:')
endfunc
" Tests for :mkspell with a .dic and .aff file
func Test_aff_file_format_error()
" FIXME: For some reason, the :mkspell command below doesn't fail on the
" MS-Windows CI build. Disable this test on MS-Windows for now.
CheckNotMSWindows
" No word count in .dic file
call writefile([], 'Xtest.dic')
call writefile([], 'Xtest.aff')
call assert_fails('mkspell! Xtest.spl Xtest', 'E760:')
" create a .dic file for the tests below
call writefile(['1', 'work'], 'Xtest.dic')
" Invalid encoding in .aff file
call writefile(['# comment', 'SET Xinvalidencoding'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Conversion in Xtest.aff not supported: from xinvalidencoding', output)
" Invalid flag in .aff file
call writefile(['FLAG xxx'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Invalid value for FLAG in Xtest.aff line 1: xxx', output)
" set FLAGS after using flag for an affix
call writefile(['SFX L Y 1', 'SFX L 0 re [^x]', 'FLAG long'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('FLAG after using flags in Xtest.aff line 3: long', output)
" INFO in affix file
let save_encoding = &encoding
call mkdir('Xrtp/spell', 'p')
call writefile(['1', 'work'], 'Xrtp/spell/Xtest.dic')
call writefile(['NAME klingon', 'VERSION 1.4', 'AUTHOR Spock'],
\ 'Xrtp/spell/Xtest.aff')
silent mkspell! Xrtp/spell/Xtest.utf-8.spl Xrtp/spell/Xtest
let save_rtp = &rtp
set runtimepath=./Xrtp
set spelllang=Xtest
set spell
let output = split(execute('spellinfo'), "\n")
call assert_equal("NAME klingon", output[1])
call assert_equal("VERSION 1.4", output[2])
call assert_equal("AUTHOR Spock", output[3])
let &rtp = save_rtp
call delete('Xrtp', 'rf')
set spell& spelllang& spellfile&
%bw!
" 'encoding' must be set again to clear the spell file in memory
let &encoding = save_encoding
" COMPOUNDFORBIDFLAG flag after PFX in an affix file
call writefile(['PFX L Y 1', 'PFX L 0 re x', 'COMPOUNDFLAG c', 'COMPOUNDFORBIDFLAG x'],
\ 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in Xtest.aff line 4', output)
" COMPOUNDPERMITFLAG flag after PFX in an affix file
call writefile(['PFX L Y 1', 'PFX L 0 re x', 'COMPOUNDPERMITFLAG c'],
\ 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in Xtest.aff line 3', output)
" Wrong COMPOUNDRULES flag value in an affix file
call writefile(['COMPOUNDRULES a'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Wrong COMPOUNDRULES value in Xtest.aff line 1: a', output)
" Wrong COMPOUNDWORDMAX flag value in an affix file
call writefile(['COMPOUNDWORDMAX 0'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Wrong COMPOUNDWORDMAX value in Xtest.aff line 1: 0', output)
" Wrong COMPOUNDMIN flag value in an affix file
call writefile(['COMPOUNDMIN 0'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Wrong COMPOUNDMIN value in Xtest.aff line 1: 0', output)
" Wrong COMPOUNDSYLMAX flag value in an affix file
call writefile(['COMPOUNDSYLMAX 0'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Wrong COMPOUNDSYLMAX value in Xtest.aff line 1: 0', output)
" Wrong CHECKCOMPOUNDPATTERN flag value in an affix file
call writefile(['CHECKCOMPOUNDPATTERN 0'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Wrong CHECKCOMPOUNDPATTERN value in Xtest.aff line 1: 0', output)
" Duplicate affix entry in an affix file
call writefile(['PFX L Y 1', 'PFX L 0 re x', 'PFX L Y 1', 'PFX L 0 re x'],
\ 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Duplicate affix in Xtest.aff line 3: L', output)
" Duplicate affix entry in an affix file
call writefile(['PFX L Y 1', 'PFX L Y 1'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Unrecognized or duplicate item in Xtest.aff line 2: PFX', output)
" Different combining flags in an affix file
call writefile(['PFX L Y 1', 'PFX L 0 re x', 'PFX L N 1'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Different combining flag in continued affix block in Xtest.aff line 3', output)
" Try to reuse a affix used for BAD flag
call writefile(['BAD x', 'PFX x Y 1', 'PFX x 0 re x'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in Xtest.aff line 2: x', output)
" Trailing characters in an affix entry
call writefile(['PFX L Y 1 Test', 'PFX L 0 re x'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Trailing text in Xtest.aff line 1: Test', output)
" Trailing characters in an affix entry
call writefile(['PFX L Y 1', 'PFX L 0 re x Test'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Trailing text in Xtest.aff line 2: Test', output)
" Incorrect combine flag in an affix entry
call writefile(['PFX L X 1', 'PFX L 0 re x'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Expected Y or N in Xtest.aff line 1: X', output)
" Invalid count for REP item
call writefile(['REP a'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Expected REP(SAL) count in Xtest.aff line 1', output)
" Trailing characters in REP item
call writefile(['REP 1', 'REP f ph test'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Trailing text in Xtest.aff line 2: test', output)
" Invalid count for MAP item
call writefile(['MAP a'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Expected MAP count in Xtest.aff line 1', output)
" Duplicate character in a MAP item
call writefile(['MAP 2', 'MAP xx', 'MAP yy'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Duplicate character in MAP in Xtest.aff line 2', output)
" Use COMPOUNDSYLMAX without SYLLABLE
call writefile(['COMPOUNDSYLMAX 2'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('COMPOUNDSYLMAX used without SYLLABLE', output)
" Missing SOFOTO
call writefile(['SOFOFROM abcdef'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Missing SOFOTO line in Xtest.aff', output)
" Length of SOFOFROM and SOFOTO differ
call writefile(['SOFOFROM abcde', 'SOFOTO ABCD'], 'Xtest.aff')
call assert_fails('mkspell! Xtest.spl Xtest', 'E759:')
" Both SAL and SOFOFROM/SOFOTO items
call writefile(['SOFOFROM abcd', 'SOFOTO ABCD', 'SAL CIA X'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Both SAL and SOFO lines in Xtest.aff', output)
" use an alphabet flag when FLAG is num
call writefile(['FLAG num', 'SFX L Y 1', 'SFX L 0 re [^x]'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Flag is not a number in Xtest.aff line 2: L', output)
" use number and alphabet flag when FLAG is num
call writefile(['FLAG num', 'SFX 4f Y 1', 'SFX 4f 0 re [^x]'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Affix name too long in Xtest.aff line 2: 4f', output)
" use a single character flag when FLAG is long
call writefile(['FLAG long', 'SFX L Y 1', 'SFX L 0 re [^x]'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('Illegal flag in Xtest.aff line 2: L', output)
" duplicate word in the .dic file
call writefile(['2', 'good', 'good', 'good'], 'Xtest.dic')
call writefile(['NAME vim'], 'Xtest.aff')
let output = execute('mkspell! Xtest.spl Xtest')
call assert_match('First duplicate word in Xtest.dic line 3: good', output)
call assert_match('2 duplicate word(s) in Xtest.dic', output)
call delete('Xtest.dic')
call delete('Xtest.aff')
call delete('Xtest.spl')
call delete('Xtest.sug')
endfunc
func Test_spell_add_word()
set spellfile=
call assert_fails('spellgood abc', 'E764:')
@@ -524,4 +733,36 @@ func Test_spell_add_word()
%bw!
endfunc
" When 'spellfile' is not set, adding a new good word will automatically set
" the 'spellfile'
func Test_init_spellfile()
let save_rtp = &rtp
let save_encoding = &encoding
call mkdir('Xrtp/spell', 'p')
call writefile(['vim'], 'Xrtp/spell/Xtest.dic')
silent mkspell Xrtp/spell/Xtest.utf-8.spl Xrtp/spell/Xtest.dic
set runtimepath=./Xrtp
set spelllang=Xtest
set spell
silent spellgood abc
call assert_equal('./Xrtp/spell/Xtest.utf-8.add', &spellfile)
call assert_equal(['abc'], readfile('Xrtp/spell/Xtest.utf-8.add'))
call assert_true(filereadable('Xrtp/spell/Xtest.utf-8.spl'))
set spell& spelllang& spellfile&
call delete('Xrtp', 'rf')
let &encoding = save_encoding
let &rtp = save_rtp
%bw!
endfunc
" Test for the 'mkspellmem' option
func Test_mkspellmem_opt()
call assert_fails('set mkspellmem=1000', 'E474:')
call assert_fails('set mkspellmem=1000,', 'E474:')
call assert_fails('set mkspellmem=1000,50', 'E474:')
call assert_fails('set mkspellmem=1000,50,', 'E474:')
call assert_fails('set mkspellmem=1000,50,10,', 'E474:')
call assert_fails('set mkspellmem=1000,50,0', 'E474:')
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+20
View File
@@ -295,5 +295,25 @@ def Test_map_command()
CheckScriptSuccess(['vim9script'] + lines)
enddef
def Test_normal_command()
new
setline(1, 'doesnotexist')
let caught = 0
try
exe "norm! \<C-]>"
catch /E433/
caught = 2
endtry
assert_equal(2, caught)
try
exe "norm! 3\<C-]>"
catch /E433/
caught = 3
endtry
assert_equal(3, caught)
bwipe!
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
+14 -1
View File
@@ -43,6 +43,9 @@ def Test_expr1()
var = 0
assert_equal('two', var ? 'one' : 'two')
# with constant condition expression is not evaluated
assert_equal('one', 1 ? 'one' : xxx)
let Some: func = function('len')
let Other: func = function('winnr')
let Res: func = g:atrue ? Some : Other
@@ -139,7 +142,6 @@ enddef
func Test_expr1_fails()
call CheckDefFailure(["let x = 1 ? 'one'"], "Missing ':' after '?'", 1)
call CheckDefFailure(["let x = 1 ? 'one' : xxx"], "E1001:", 1)
let msg = "white space required before and after '?'"
call CheckDefFailure(["let x = 1? 'one' : 'two'"], msg, 1)
@@ -1668,6 +1670,17 @@ def Test_expr7_lambda_vim9script()
CheckScriptSuccess(lines)
enddef
def Test_epxr7_funcref()
let lines =<< trim END
def RetNumber(): number
return 123
enddef
let FuncRef = RetNumber
assert_equal(123, FuncRef())
END
CheckDefAndScriptSuccess(lines)
enddef
def Test_expr7_dict()
# dictionary
assert_equal(g:dict_empty, {})
+57 -19
View File
@@ -15,6 +15,18 @@ def Test_range_only()
setline(1, ['blah', 'Blah'])
:/Blah/
assert_equal(2, getcurpos()[1])
bwipe!
# without range commands use current line
new
setline(1, ['one', 'two', 'three'])
:2
print
assert_equal('two', Screenline(&lines))
:3
list
assert_equal('three$', Screenline(&lines))
bwipe!
enddef
let s:appendToMe = 'xxx'
@@ -110,12 +122,21 @@ def Test_assignment()
endif
lines =<< trim END
vim9script
&ts = 6
&ts += 3
assert_equal(9, &ts)
&l:ts = 6
assert_equal(6, &ts)
&l:ts += 2
assert_equal(8, &ts)
&g:ts = 6
assert_equal(6, &g:ts)
&g:ts += 2
assert_equal(8, &g:ts)
END
CheckScriptSuccess(lines)
CheckDefAndScriptSuccess(lines)
CheckDefFailure(['&notex += 3'], 'E113:')
CheckDefFailure(['&ts ..= "xxx"'], 'E1019:')
@@ -163,19 +184,15 @@ def Test_assignment()
call CheckDefFailure(['$SOME_ENV_VAR += "more"'], 'E1051:')
call CheckDefFailure(['$SOME_ENV_VAR += 123'], 'E1012:')
@a = 'areg'
@a ..= 'add'
assert_equal('aregadd', @a)
call CheckDefFailure(['@a += "more"'], 'E1051:')
call CheckDefFailure(['@a += 123'], 'E1012:')
lines =<< trim END
vim9script
@c = 'areg'
@c ..= 'add'
assert_equal('aregadd', @c)
END
call CheckScriptSuccess(lines)
CheckDefAndScriptSuccess(lines)
call CheckDefFailure(['@a += "more"'], 'E1051:')
call CheckDefFailure(['@a += 123'], 'E1012:')
v:errmsg = 'none'
v:errmsg ..= 'again'
@@ -608,6 +625,13 @@ def Test_unlet()
assert_false(exists('g:somevar'))
unlet! g:somevar
# also works for script-local variable in legacy Vim script
s:somevar = 'legacy'
assert_true(exists('s:somevar'))
unlet s:somevar
assert_false(exists('s:somevar'))
unlet! s:somevar
call CheckScriptFailure([
'vim9script',
'let svar = 123',
@@ -1660,8 +1684,9 @@ def Test_vim9script_funcref()
delete('Xscript.vim')
enddef
" Check that when searcing for "FilterFunc" it doesn't find the import in the
" script where FastFilter() is called from.
" Check that when searching for "FilterFunc" it finds the import in the
" script where FastFilter() is called from, both as a string and as a direct
" function reference.
def Test_vim9script_funcref_other_script()
let filterLines =<< trim END
vim9script
@@ -1671,22 +1696,26 @@ def Test_vim9script_funcref_other_script()
export def FastFilter(): list<number>
return range(10)->filter('FilterFunc')
enddef
export def FastFilterDirect(): list<number>
return range(10)->filter(FilterFunc)
enddef
END
writefile(filterLines, 'Xfilter.vim')
let lines =<< trim END
vim9script
import {FilterFunc, FastFilter} from './Xfilter.vim'
import {FilterFunc, FastFilter, FastFilterDirect} from './Xfilter.vim'
def Test()
let x: list<number> = FastFilter()
enddef
Test()
def TestDirect()
let x: list<number> = FastFilterDirect()
enddef
TestDirect()
END
writefile(lines, 'Ximport.vim')
assert_fails('source Ximport.vim', 'E121:')
CheckScriptSuccess(lines)
delete('Xfilter.vim')
delete('Ximport.vim')
enddef
def Test_vim9script_reload_delfunc()
@@ -2093,6 +2122,14 @@ def Test_if_const_expr()
res = true
endif
assert_equal(false, res)
# with constant "false" expression may be invalid so long as the syntax is OK
if false | eval 0 | endif
if false | eval burp + 234 | endif
if false | echo burp 234 'asd' | endif
if false
burp
endif
enddef
def Test_if_const_expr_fails()
@@ -2152,8 +2189,9 @@ def Test_execute_cmd()
echomsg [1, 2, 3] #{a: 1, b: 2}
assert_match('^\[1, 2, 3\] {''a'': 1, ''b'': 2}$', Screenline(&lines))
call CheckDefFailure(['execute xxx'], 'E1001:')
call CheckDefFailure(['execute "cmd"# comment'], 'E488:')
call CheckDefFailure(['execute xxx'], 'E1001:', 1)
call CheckDefExecFailure(['execute "tabnext " .. 8'], 'E475:', 1)
call CheckDefFailure(['execute "cmd"# comment'], 'E488:', 1)
enddef
def Test_execute_cmd_vimscript()
+5
View File
@@ -41,6 +41,11 @@ def CheckScriptSuccess(lines: list<string>)
delete('Xdef')
enddef
def CheckDefAndScriptSuccess(lines: list<string>)
CheckDefSuccess(lines)
CheckScriptSuccess(['vim9script'] + lines)
enddef
" Check that a command fails both when used in a :def function and when used
" in Vim9 script.
def CheckScriptAndDefFailure(lines: list<string>, error: string, lnum = -3)
+1 -1
View File
@@ -548,7 +548,7 @@ ui_delay(long msec_arg, int ignoreinput)
if (gui.in_use)
gui_macvim_force_flush();
#endif
mch_delay(msec, ignoreinput);
mch_delay(msec, ignoreinput ? MCH_DELAY_IGNOREINPUT : 0);
}
}
+38
View File
@@ -769,6 +769,44 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1531,
/**/
1530,
/**/
1529,
/**/
1528,
/**/
1527,
/**/
1526,
/**/
1525,
/**/
1524,
/**/
1523,
/**/
1522,
/**/
1521,
/**/
1520,
/**/
1519,
/**/
1518,
/**/
1517,
/**/
1516,
/**/
1515,
/**/
1514,
/**/
1513,
/**/
1512,
/**/
+4
View File
@@ -2679,4 +2679,8 @@ long elapsed(DWORD start_tick);
#define READDIR_SORT_IC 2 // sort ignoring case (strcasecmp)
#define READDIR_SORT_COLLATE 3 // sort according to collation (strcoll)
// Flags for mch_delay.
#define MCH_DELAY_IGNOREINPUT 1
#define MCH_DELAY_SETTMODE 2
#endif // VIM__H
+55 -30
View File
@@ -258,6 +258,15 @@ lookup_arg(
return FAIL;
}
/*
* Returnd TRUE if the script context is Vim9 script.
*/
static int
script_is_vim9()
{
return SCRIPT_ITEM(current_sctx.sc_sid)->sn_version == SCRIPT_VERSION_VIM9;
}
/*
* Lookup a variable in the current script.
* If "vim9script" is TRUE the script must be Vim9 script. Used for "var"
@@ -271,8 +280,7 @@ lookup_script(char_u *name, size_t len, int vim9script)
hashtab_T *ht = &SCRIPT_VARS(current_sctx.sc_sid);
dictitem_T *di;
if (vim9script && SCRIPT_ITEM(current_sctx.sc_sid)->sn_version
!= SCRIPT_VERSION_VIM9)
if (vim9script && !script_is_vim9())
return FAIL;
cc = name[len];
name[len] = NUL;
@@ -4006,6 +4014,13 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
int ppconst_used = ppconst->pp_used;
char_u *next;
// Ignore all kinds of errors when not producing code.
if (cctx->ctx_skip == SKIP_YES)
{
skip_expr(arg);
return OK;
}
// Evaluate the first expression.
if (compile_expr2(arg, cctx, ppconst) == FAIL)
return FAIL;
@@ -4256,7 +4271,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
ufunc = def_function(eap, lambda_name);
if (ufunc == NULL)
return NULL;
return eap->skip ? (char_u *)"" : NULL;
if (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, TRUE, cctx) == FAIL)
return NULL;
@@ -4542,8 +4557,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
p = var_start + 2;
else
{
p = (*var_start == '&' || *var_start == '$')
? var_start + 1 : var_start;
// skip over the leading "&", "&l:", "&g:" and "$"
p = skip_option_env_lead(var_start);
p = to_name_end(p, TRUE);
}
@@ -4587,8 +4602,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
}
cc = *p;
*p = NUL;
opt_type = get_option_value(var_start + 1, &numval,
NULL, opt_flags);
opt_type = get_option_value(skip_option_env_lead(var_start),
&numval, NULL, opt_flags);
*p = cc;
if (opt_type == -3)
{
@@ -5123,7 +5138,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
switch (dest)
{
case dest_option:
generate_STOREOPT(cctx, name + 1, opt_flags);
generate_STOREOPT(cctx, skip_option_env_lead(name),
opt_flags);
break;
case dest_global:
// include g: with the name, easier to execute that way
@@ -5234,6 +5250,9 @@ check_vim9_unlet(char_u *name)
{
if (name[1] != ':' || vim_strchr((char_u *)"gwtb", *name) == NULL)
{
// "unlet s:var" is allowed in legacy script.
if (*name == 's' && !script_is_vim9())
return OK;
semsg(_(e_cannot_unlet_str), name);
return FAIL;
}
@@ -5500,6 +5519,7 @@ compile_elseif(char_u *arg, cctx_T *cctx)
isn_T *isn;
scope_T *scope = cctx->ctx_scope;
ppconst_T ppconst;
skip_T save_skip = cctx->ctx_skip;
if (scope == NULL || scope->se_type != IF_SCOPE)
{
@@ -5522,11 +5542,14 @@ compile_elseif(char_u *arg, cctx_T *cctx)
// compile "expr"; if we know it evaluates to FALSE skip the block
CLEAR_FIELD(ppconst);
if (cctx->ctx_skip == SKIP_YES)
cctx->ctx_skip = SKIP_UNKNOWN;
if (compile_expr1(&p, cctx, &ppconst) == FAIL)
{
clear_ppconst(&ppconst);
return NULL;
}
cctx->ctx_skip = save_skip;
if (scope->se_skip_save == SKIP_YES)
clear_ppconst(&ppconst);
else if (instr->ga_len == instr_count && ppconst.pp_used == 1)
@@ -6712,17 +6735,8 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
p = skipwhite(p);
if (cctx.ctx_skip == SKIP_YES
&& ea.cmdidx != CMD_if
if (cctx.ctx_had_return
&& ea.cmdidx != CMD_elseif
&& ea.cmdidx != CMD_else
&& ea.cmdidx != CMD_endif)
{
line = (char_u *)"";
continue;
}
if (ea.cmdidx != CMD_elseif
&& ea.cmdidx != CMD_else
&& ea.cmdidx != CMD_endif
&& ea.cmdidx != CMD_endfor
@@ -6731,11 +6745,8 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
&& ea.cmdidx != CMD_finally
&& ea.cmdidx != CMD_endtry)
{
if (cctx.ctx_had_return)
{
emsg(_(e_unreachable_code_after_return));
goto erret;
}
emsg(_(e_unreachable_code_after_return));
goto erret;
}
switch (ea.cmdidx)
@@ -6833,7 +6844,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
if (compile_expr0(&p, &cctx) == FAIL)
goto erret;
// drop the return value
// drop the result
generate_instr_drop(&cctx, ISN_DROP, 1);
line = skipwhite(p);
@@ -6847,7 +6858,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
line = compile_mult_expr(p, ea.cmdidx, &cctx);
break;
// TODO: other commands with an expression argument
// TODO: any other commands with an expression argument?
case CMD_append:
case CMD_change:
@@ -6858,13 +6869,27 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
goto erret;
case CMD_SIZE:
semsg(_(e_invalid_command_str), ea.cmd);
goto erret;
if (cctx.ctx_skip != SKIP_YES)
{
semsg(_(e_invalid_command_str), ea.cmd);
goto erret;
}
// We don't check for a next command here.
line = (char_u *)"";
break;
default:
// Not recognized, execute with do_cmdline_cmd().
ea.arg = p;
line = compile_exec(line, &ea, &cctx);
if (cctx.ctx_skip == SKIP_YES)
{
// We don't check for a next command here.
line = (char_u *)"";
}
else
{
// Not recognized, execute with do_cmdline_cmd().
ea.arg = p;
line = compile_exec(line, &ea, &cctx);
}
break;
}
nextline:
+3
View File
@@ -1062,7 +1062,10 @@ call_def_function(
if (ga.ga_data != NULL)
{
if (iptr->isn_type == ISN_EXECUTE)
{
SOURCING_LNUM = iptr->isn_lnum;
do_cmdline_cmd((char_u *)ga.ga_data);
}
else
{
msg_sb_eol();