Compare commits

...

10 Commits

Author SHA1 Message Date
Bram Moolenaar
acbae18df5 patch 8.2.2139: Vim9: unreachable code in assignment
Problem:    Vim9: unreachable code in assignment.
Solution:   Don't check "new_local" when "has_index" is set.  Add test for
            wrong type of list index.
2020-12-13 18:44:43 +01:00
Bram Moolenaar
b5b9480ee9 patch 8.2.2138: Vim9: "exit_cb" causes Vim to exit
Problem:    Vim9: "exit_cb" causes Vim to exit.
Solution:   Require white space after a command in Vim9 script. (closes #7467)
            Also fix that Vim9 style heredoc was not always recognized.
2020-12-13 17:50:20 +01:00
Bram Moolenaar
e498429087 patch 8.2.2137: Vim9: :echo and :execute give error for empty argument
Problem:    Vim9: :echo and :execute give error for empty argument.
Solution:   Ignore an empty argument. (closes #7468)
2020-12-13 14:19:25 +01:00
Bram Moolenaar
c530852315 patch 8.2.2136: Vim9: Using uninitialized variable
Problem:    Vim9: Using uninitialized variable.
Solution:   Initialize "len" to zero.  Clean up fnamemodify().
2020-12-13 12:25:35 +01:00
Bram Moolenaar
93f82cbee5 patch 8.2.2135: Vim9: #{ still seen as start of dict in some places
Problem:    Vim9: #{ still seen as start of dict in some places.
Solution:   Remove check for { after #. (closes #7456)
2020-12-12 21:25:56 +01:00
Bram Moolenaar
57f799e6a4 patch 8.2.2134: Vim9: get E1099 when autocmd triggered in builtin function
Problem:    Vim9: get E1099 when autocmd triggered in builtin function.
Solution:   Check that did_emsg increased instead of checking that it changed.
            (closes #7448)
2020-12-12 20:42:19 +01:00
Bram Moolenaar
2a9d5d386b patch 8.2.2133: Vim9: checking for a non-empty string is too strict
Problem:    Vim9: checking for a non-empty string is too strict.
Solution:   Check for any string. (closes #7447)
2020-12-12 18:58:40 +01:00
Bram Moolenaar
3ae50c775c patch 8.2.2132: padding not drawn properly for popup window with title
Problem:    Padding not drawn properly for popup window with title.
Solution:   Draw the padding below the title. (closes #7460)
2020-12-12 18:18:06 +01:00
Bram Moolenaar
709664cca0 patch 8.2.2131: Vim9: crash when lambda uses same var as assignment
Problem:    Vim9: crash when lambda uses same var as assignment.
Solution:   Do not let lookup_local change lv_from_outer, make a copy.
            (closes #7461)
2020-12-12 14:33:41 +01:00
Bram Moolenaar
cc2335896b patch 8.2.2130: Insert mode completion messages end up in message history
Problem:    Insert mode completion messages end up in message history.
Solution:   Set msg_hist_off. (closes #7452
2020-12-12 13:32:07 +01:00
25 changed files with 355 additions and 131 deletions

View File

@@ -61,7 +61,7 @@ EXTERN char e_argument_nr_type_mismatch_expected_str_but_got_str[]
INIT(= N_("E1013: Argument %d: type mismatch, expected %s but got %s"));
EXTERN char e_invalid_key_str[]
INIT(= N_("E1014: Invalid key: %s"));
EXTERN char e_name_expected[]
EXTERN char e_name_expected_str[]
INIT(= N_("E1015: Name expected: %s"));
EXTERN char e_cannot_declare_a_scope_variable[]
INIT(= N_("E1016: Cannot declare a %s variable: %s"));
@@ -313,3 +313,11 @@ EXTERN char e_for_argument_must_be_sequence_of_lists[]
INIT(= N_("E1140: For argument must be a sequence of lists"));
EXTERN char e_indexable_type_required[]
INIT(= N_("E1141: Indexable type required"));
EXTERN char e_non_empty_string_required[]
INIT(= N_("E1142: Non-empty string required"));
EXTERN char e_empty_expression_str[]
INIT(= N_("E1143: Empty expression: \"%s\""));
EXTERN char e_command_not_followed_by_white_space_str[]
INIT(= N_("E1144: Command is not followed by white space: %s"));
EXTERN char e_missing_heredoc_end_marker_str[]
INIT(= N_("E1145: Missing heredoc end marker: %s"));

View File

@@ -2721,19 +2721,23 @@ get_script_local_ht(void)
/*
* Look for "name[len]" in script-local variables.
* Return a non-NULL pointer when found, NULL when not found.
* Return OK when found, FAIL when not found.
*/
void *
lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED)
int
lookup_scriptvar(
char_u *name,
size_t len,
void *lvar UNUSED,
cctx_T *dummy UNUSED)
{
hashtab_T *ht = get_script_local_ht();
char_u buffer[30];
char_u *p;
void *res;
int res;
hashitem_T *hi;
if (ht == NULL)
return NULL;
return FAIL;
if (len < sizeof(buffer) - 1)
{
// avoid an alloc/free for short names
@@ -2744,20 +2748,19 @@ lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED)
{
p = vim_strnsave(name, len);
if (p == NULL)
return NULL;
return FAIL;
}
hi = hash_find(ht, p);
res = HASHITEM_EMPTY(hi) ? NULL : hi;
res = HASHITEM_EMPTY(hi) ? FAIL : OK;
// if not script-local, then perhaps imported
if (res == NULL && find_imported(p, 0, NULL) != NULL)
res = p;
if (res == FAIL && find_imported(p, 0, NULL) != NULL)
res = OK;
if (p != buffer)
vim_free(p);
// Don't return "buffer", gcc complains.
return res == NULL ? NULL : IObuff;
return res;
}
/*

View File

@@ -55,6 +55,7 @@
#define EX_LOCK_OK 0x1000000 // command can be executed when textlock is
// set; when missing disallows editing another
// buffer when curbuf_lock is set
#define EX_NONWHITE_OK 0x2000000 // command can be followed by non-white
#define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed
#define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file
@@ -632,7 +633,7 @@ EXCMD(CMD_function, "function", ex_function,
EX_EXTRA|EX_BANG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_global, "global", ex_global,
EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_EXTRA|EX_DFLALL|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_EXTRA|EX_DFLALL|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK,
ADDR_LINES),
EXCMD(CMD_goto, "goto", ex_goto,
EX_RANGE|EX_COUNT|EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
@@ -1277,7 +1278,7 @@ EXCMD(CMD_rviminfo, "rviminfo", ex_viminfo,
EX_BANG|EX_FILE1|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_substitute, "substitute", ex_substitute,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK,
ADDR_LINES),
EXCMD(CMD_sNext, "sNext", ex_previous,
EX_EXTRA|EX_RANGE|EX_COUNT|EX_BANG|EX_CMDARG|EX_ARGOPT|EX_TRLBAR,
@@ -1652,7 +1653,7 @@ EXCMD(CMD_update, "update", ex_update,
EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_FILE1|EX_ARGOPT|EX_DFLALL|EX_TRLBAR,
ADDR_LINES),
EXCMD(CMD_vglobal, "vglobal", ex_global,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_DFLALL|EX_CMDWIN|EX_LOCK_OK,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_DFLALL|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK,
ADDR_LINES),
EXCMD(CMD_var, "var", ex_var,
EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
@@ -1792,16 +1793,16 @@ EXCMD(CMD_z, "z", ex_z,
// commands that don't start with a letter
EXCMD(CMD_bang, "!", ex_bang,
EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_FILES|EX_CMDWIN|EX_LOCK_OK,
EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_FILES|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK,
ADDR_LINES),
EXCMD(CMD_pound, "#", ex_print,
EX_RANGE|EX_WHOLEFOLD|EX_COUNT|EX_FLAGS|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
ADDR_LINES),
EXCMD(CMD_and, "&", ex_substitute,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY|EX_NONWHITE_OK,
ADDR_LINES),
EXCMD(CMD_star, "*", ex_at,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK,
ADDR_LINES),
EXCMD(CMD_lshift, "<", ex_operators,
EX_RANGE|EX_WHOLEFOLD|EX_COUNT|EX_FLAGS|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY,
@@ -1813,7 +1814,7 @@ EXCMD(CMD_rshift, ">", ex_operators,
EX_RANGE|EX_WHOLEFOLD|EX_COUNT|EX_FLAGS|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY,
ADDR_LINES),
EXCMD(CMD_at, "@", ex_at,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_NONWHITE_OK,
ADDR_LINES),
EXCMD(CMD_block, "{{{{{{{{", ex_block, // not found normally
0,
@@ -1822,7 +1823,7 @@ EXCMD(CMD_endblock, "}", ex_endblock,
EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_tilde, "~", ex_substitute,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY,
EX_RANGE|EX_WHOLEFOLD|EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY|EX_NONWHITE_OK,
ADDR_LINES),
// commands that start with an uppercase letter

View File

@@ -1683,7 +1683,7 @@ comment_start(char_u *p, int starts_with_colon UNUSED)
{
#ifdef FEAT_EVAL
if (in_vim9script())
return p[0] == '#' && p[1] != '{' && !starts_with_colon;
return p[0] == '#' && !starts_with_colon;
#endif
return *p == '"';
}
@@ -3271,7 +3271,7 @@ skip_option_env_lead(char_u *start)
find_ex_command(
exarg_T *eap,
int *full UNUSED,
void *(*lookup)(char_u *, size_t, cctx_T *) UNUSED,
int (*lookup)(char_u *, size_t, void *, cctx_T *) UNUSED,
cctx_T *cctx UNUSED)
{
int len;
@@ -3387,7 +3387,7 @@ find_ex_command(
|| *eap->cmd == '&'
|| *eap->cmd == '$'
|| *eap->cmd == '@'
|| lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
|| lookup(eap->cmd, p - eap->cmd, NULL, cctx) == OK)
{
eap->cmdidx = CMD_var;
return eap->cmd;
@@ -3528,6 +3528,14 @@ find_ex_command(
if (eap->cmdidx == CMD_final && p - eap->cmd == 4)
eap->cmdidx = CMD_finally;
if (eap->cmdidx != CMD_SIZE && in_vim9script()
&& !IS_WHITE_OR_NUL(*p) && !ends_excmd(*p) && *p != '!'
&& (cmdnames[eap->cmdidx].cmd_argt & EX_NONWHITE_OK) == 0)
{
semsg(_(e_command_not_followed_by_white_space_str), eap->cmd);
eap->cmdidx = CMD_SIZE;
}
return p;
}
@@ -4780,7 +4788,6 @@ separate_nextcmd(exarg_T *eap)
|| (*p == '#'
&& in_vim9script()
&& !(eap->argt & EX_NOTRLCOM)
&& p[1] != '{'
&& p > eap->cmd && VIM_ISWHITE(p[-1]))
#endif
|| *p == '|' || *p == '\n')
@@ -5115,7 +5122,7 @@ ex_blast(exarg_T *eap)
/*
* Check if "c" ends an Ex command.
* In Vim9 script does not check for white space before # or #{.
* In Vim9 script does not check for white space before #.
*/
int
ends_excmd(int c)

View File

@@ -876,7 +876,7 @@ f_exepath(typval_T *argvars, typval_T *rettv)
{
char_u *p = NULL;
if (in_vim9script() && check_for_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_nonempty_string(&argvars[0]) == FAIL)
return;
(void)mch_can_exe(tv_get_string(&argvars[0]), &p, TRUE);
rettv->v_type = VAR_STRING;
@@ -942,7 +942,7 @@ findfilendir(
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
if (in_vim9script() && check_for_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_nonempty_string(&argvars[0]) == FAIL)
return;
#ifdef FEAT_SEARCHPATH
@@ -1019,7 +1019,7 @@ f_fnamemodify(typval_T *argvars, typval_T *rettv)
char_u *fname;
char_u *mods;
int usedlen = 0;
int len;
int len = 0;
char_u *fbuf = NULL;
char_u buf[NUMBUFLEN];
@@ -1028,12 +1028,13 @@ f_fnamemodify(typval_T *argvars, typval_T *rettv)
return;
fname = tv_get_string_chk(&argvars[0]);
mods = tv_get_string_buf_chk(&argvars[1], buf);
if (fname == NULL || mods == NULL)
if (mods == NULL || fname == NULL)
fname = NULL;
else
{
len = (int)STRLEN(fname);
(void)modify_fname(mods, FALSE, &usedlen, &fname, &fbuf, &len);
if (mods != NULL && *mods != NUL)
(void)modify_fname(mods, FALSE, &usedlen, &fname, &fbuf, &len);
}
rettv->v_type = VAR_STRING;

View File

@@ -1298,6 +1298,7 @@ ins_compl_files(
fp = mch_fopen((char *)files[i], "r"); // open dictionary file
if (flags != DICT_EXACT)
{
msg_hist_off = TRUE; // reset in msg_trunc_attr()
vim_snprintf((char *)IObuff, IOSIZE,
_("Scanning dictionary: %s"), (char *)files[i]);
(void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
@@ -2778,6 +2779,7 @@ ins_compl_get_exp(pos_T *ini)
dict = ins_buf->b_fname;
dict_f = DICT_EXACT;
}
msg_hist_off = TRUE; // reset in msg_trunc_attr()
vim_snprintf((char *)IObuff, IOSIZE, _("Scanning: %s"),
ins_buf->b_fname == NULL
? buf_spname(ins_buf)
@@ -2812,6 +2814,7 @@ ins_compl_get_exp(pos_T *ini)
#endif
else if (*e_cpt == ']' || *e_cpt == 't')
{
msg_hist_off = TRUE; // reset in msg_trunc_attr()
type = CTRL_X_TAGS;
vim_snprintf((char *)IObuff, IOSIZE, _("Scanning tags."));
(void)msg_trunc_attr((char *)IObuff, TRUE, HL_ATTR(HLF_R));
@@ -3423,9 +3426,11 @@ ins_compl_next(
MB_PTR_ADV(s);
}
}
msg_hist_off = TRUE;
vim_snprintf((char *)IObuff, IOSIZE, "%s %s%s", lead,
s > compl_shown_match->cp_fname ? "<" : "", s);
msg((char *)IObuff);
msg_hist_off = FALSE;
redraw_cmdline = FALSE; // don't overwrite!
}
}
@@ -4105,9 +4110,13 @@ ins_complete(int c, int enable_pum)
if (edit_submode_extra != NULL)
{
if (!p_smd)
{
msg_hist_off = TRUE;
msg_attr((char *)edit_submode_extra,
edit_submode_highl < HLF_COUNT
? HL_ATTR(edit_submode_highl) : 0);
msg_hist_off = FALSE;
}
}
else
msg_clr_cmdline(); // necessary for "noshowmode"

View File

@@ -3868,20 +3868,19 @@ update_popups(void (*win_update)(win_T *wp))
}
if (top_padding > 0)
{
// top padding; do not draw over the title
row = wp->w_winrow + wp->w_popup_border[0];
if (title_len > 0)
if (title_len > 0 && row == wp->w_winrow)
{
screen_fill(row, row + top_padding, padcol, title_wincol,
// top padding and no border; do not draw over the title
screen_fill(row, row + 1, padcol, title_wincol,
' ', ' ', popup_attr);
screen_fill(row, row + top_padding, title_wincol + title_len,
screen_fill(row, row + 1, title_wincol + title_len,
padendcol, ' ', ' ', popup_attr);
row += 1;
top_padding -= 1;
}
else
{
screen_fill(row, row + top_padding, padcol, padendcol,
screen_fill(row, row + top_padding, padcol, padendcol,
' ', ' ', popup_attr);
}
}
// Compute scrollbar thumb position and size.

View File

@@ -59,7 +59,7 @@ void check_vars(char_u *name, int len);
dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload);
hashtab_T *get_script_local_ht(void);
void *lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy);
int lookup_scriptvar(char_u *name, size_t len, void *lvar, cctx_T *dummy);
hashtab_T *find_var_ht(char_u *name, char_u **varname);
char_u *get_var_value(char_u *name);
void new_script_vars(scid_T id);

View File

@@ -13,7 +13,7 @@ void undo_cmdmod(cmdmod_T *cmod);
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);
char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, void *, cctx_T *), cctx_T *cctx);
int modifier_len(char_u *cmd);
int cmd_exists(char_u *name);
cmdidx_T excmd_get_cmdidx(char_u *cmd, int len);

View File

@@ -10,6 +10,7 @@ varnumber_T tv_get_bool(typval_T *varp);
varnumber_T tv_get_bool_chk(typval_T *varp, int *denote);
float_T tv_get_float(typval_T *varp);
int check_for_string(typval_T *tv);
int check_for_nonempty_string(typval_T *tv);
char_u *tv_get_string(typval_T *varp);
char_u *tv_get_string_buf(typval_T *varp, char_u *buf);
char_u *tv_get_string_chk(typval_T *varp);

View File

@@ -0,0 +1,10 @@
>1+0&#ffffff0| @73
|2| @73
|3| @27| +0#0000001#ffd7ff255|T|i|t|l|e| @9| +0#0000000#ffffff0@29
|4| @27| +0#0000001#ffd7ff255@15| +0#0000000#ffffff0@29
|5| @27| +0#0000001#ffd7ff255@1|a@2| @10| +0#0000000#ffffff0@29
|6| @27| +0#0000001#ffd7ff255@1|b@2| @10| +0#0000000#ffffff0@29
|7| @27| +0#0000001#ffd7ff255@15| +0#0000000#ffffff0@29
|8| @27| +0#0000001#ffd7ff255@15| +0#0000000#ffffff0@29
|9| @73
|:| @55|1|,|1| @10|T|o|p|

View File

@@ -0,0 +1,10 @@
>1+0&#ffffff0| @73
|2| @26|╔+0#0000001#ffd7ff255|T|i|t|l|e|═@10|╗| +0#0000000#ffffff0@28
|3| @26|║+0#0000001#ffd7ff255| @15|║| +0#0000000#ffffff0@28
|4| @26|║+0#0000001#ffd7ff255| @15|║| +0#0000000#ffffff0@28
|5| @26|║+0#0000001#ffd7ff255| @1|a@2| @10|║| +0#0000000#ffffff0@28
|6| @26|║+0#0000001#ffd7ff255| @1|b@2| @10|║| +0#0000000#ffffff0@28
|7| @26|║+0#0000001#ffd7ff255| @15|║| +0#0000000#ffffff0@28
|8| @26|║+0#0000001#ffd7ff255| @15|║| +0#0000000#ffffff0@28
|9| @26|╚+0#0000001#ffd7ff255|═@15|╝| +0#0000000#ffffff0@28
|:| @55|1|,|1| @10|T|o|p|

View File

@@ -114,7 +114,7 @@ func Test_omni_dash()
set omnifunc=Omni
new
exe "normal Gofind -\<C-x>\<C-o>"
call assert_equal("\n-\nmatch 1 of 2", execute(':2mess'))
call assert_equal("find -help", getline('$'))
bwipe!
delfunc Omni
@@ -714,4 +714,16 @@ func Test_issue_7021()
set completeslash=
endfunc
" Test to ensure 'Scanning...' messages are not recorded in messages history
func Test_z1_complete_no_history()
new
messages clear
let currmess = execute('messages')
setlocal dictionary=README.txt
exe "normal owh\<C-X>\<C-K>"
exe "normal owh\<C-N>"
call assert_equal(currmess, execute('messages'))
close!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -338,7 +338,7 @@ func Test_let_heredoc_fails()
endfunc
END
call writefile(text, 'XheredocFail')
call assert_fails('source XheredocFail', 'E126:')
call assert_fails('source XheredocFail', 'E1145:')
call delete('XheredocFail')
let text =<< trim CodeEnd
@@ -347,7 +347,7 @@ func Test_let_heredoc_fails()
endfunc
CodeEnd
call writefile(text, 'XheredocWrong')
call assert_fails('source XheredocWrong', 'E126:')
call assert_fails('source XheredocWrong', 'E1145:')
call delete('XheredocWrong')
let text =<< trim TEXTend

View File

@@ -1766,6 +1766,16 @@ func Test_popup_title()
call term_sendkeys(buf, ":\<CR>")
call VerifyScreenDump(buf, 'Test_popupwin_longtitle_2', {})
call term_sendkeys(buf, ":call popup_clear()\<CR>")
call term_sendkeys(buf, ":call popup_create(['aaa', 'bbb'], #{title: 'Title', minwidth: 12, padding: [2, 2, 2, 2]})\<CR>")
call term_sendkeys(buf, ":\<CR>")
call VerifyScreenDump(buf, 'Test_popupwin_longtitle_3', {})
call term_sendkeys(buf, ":call popup_clear()\<CR>")
call term_sendkeys(buf, ":call popup_create(['aaa', 'bbb'], #{title: 'Title', minwidth: 12, border: [], padding: [2, 2, 2, 2]})\<CR>")
call term_sendkeys(buf, ":\<CR>")
call VerifyScreenDump(buf, 'Test_popupwin_longtitle_4', {})
" clean up
call StopVimInTerminal(buf)
call delete('XtestPopupTitle')

View File

@@ -326,6 +326,18 @@ def Test_assign_index()
END
CheckDefFailure(lines, 'E1012: Type mismatch; expected number but got dict<unknown>', 3)
lines =<< trim END
var lines: list<string>
lines['a'] = 'asdf'
END
CheckDefFailure(lines, 'E39:', 2)
lines =<< trim END
var lines: string
lines[9] = 'asdf'
END
CheckDefFailure(lines, 'E1141:', 2)
# list of dict
var ld: list<dict<number>>
ld[0] = {}
@@ -982,6 +994,17 @@ def Test_heredoc()
var&lines =<< trim END
x
x
enddef
defcompile
[END]
CheckScriptFailure(lines, 'E1145: Missing heredoc end marker: END')
delfunc! g:Func
lines =<< trim [END]
def Func()
var lines =<< trim END
x
x
x
x
x
@@ -991,7 +1014,7 @@ def Test_heredoc()
enddef
call Func()
[END]
CheckScriptFailure(lines, 'E990:')
CheckScriptFailure(lines, 'E1145: Missing heredoc end marker: END')
delfunc! g:Func
enddef

View File

@@ -186,15 +186,17 @@ def Test_count()
enddef
def Test_executable()
assert_false(executable(""))
assert_false(executable(test_null_string()))
CheckDefExecFailure(['echo executable(123)'], 'E928:')
CheckDefExecFailure(['echo executable(true)'], 'E928:')
CheckDefExecFailure(['echo executable(v:null)'], 'E928:')
CheckDefExecFailure(['echo executable("")'], 'E928:')
enddef
def Test_exepath()
CheckDefExecFailure(['echo exepath(true)'], 'E928:')
CheckDefExecFailure(['echo exepath(v:null)'], 'E928:')
CheckDefExecFailure(['echo exepath("")'], 'E928:')
CheckDefExecFailure(['echo exepath("")'], 'E1142:')
enddef
def Test_expand()
@@ -254,36 +256,42 @@ def Test_map_function_arg()
enddef
def Test_filereadable()
assert_false(filereadable(""))
assert_false(filereadable(test_null_string()))
CheckDefExecFailure(['echo filereadable(123)'], 'E928:')
CheckDefExecFailure(['echo filereadable(true)'], 'E928:')
CheckDefExecFailure(['echo filereadable(v:null)'], 'E928:')
CheckDefExecFailure(['echo filereadable("")'], 'E928:')
enddef
def Test_filewritable()
assert_false(filewritable(""))
assert_false(filewritable(test_null_string()))
CheckDefExecFailure(['echo filewritable(123)'], 'E928:')
CheckDefExecFailure(['echo filewritable(true)'], 'E928:')
CheckDefExecFailure(['echo filewritable(v:null)'], 'E928:')
CheckDefExecFailure(['echo filewritable("")'], 'E928:')
enddef
def Test_finddir()
CheckDefExecFailure(['echo finddir(true)'], 'E928:')
CheckDefExecFailure(['echo finddir(v:null)'], 'E928:')
CheckDefExecFailure(['echo finddir("")'], 'E928:')
CheckDefExecFailure(['echo finddir("")'], 'E1142:')
enddef
def Test_findfile()
CheckDefExecFailure(['echo findfile(true)'], 'E928:')
CheckDefExecFailure(['echo findfile(v:null)'], 'E928:')
CheckDefExecFailure(['echo findfile("")'], 'E928:')
CheckDefExecFailure(['echo findfile("")'], 'E1142:')
enddef
def Test_fnamemodify()
CheckDefSuccess(['echo fnamemodify(test_null_string(), ":p")'])
CheckDefSuccess(['echo fnamemodify("", ":p")'])
CheckDefSuccess(['echo fnamemodify("file", test_null_string())'])
CheckDefSuccess(['echo fnamemodify("file", "")'])
CheckDefExecFailure(['echo fnamemodify(true, ":p")'], 'E928:')
CheckDefExecFailure(['echo fnamemodify(v:null, ":p")'], 'E928:')
CheckDefExecFailure(['echo fnamemodify("", ":p")'], 'E928:')
CheckDefExecFailure(['echo fnamemodify("file", true)'], 'E928:')
CheckDefExecFailure(['echo fnamemodify("file", v:null)'], 'E928:')
CheckDefExecFailure(['echo fnamemodify("file", "")'], 'E928:')
enddef
def Test_filter_wrong_dict_key_type()
@@ -359,27 +367,35 @@ def Test_getloclist_return_type()
enddef
def Test_getfperm()
assert_equal('', getfperm(""))
assert_equal('', getfperm(test_null_string()))
CheckDefExecFailure(['echo getfperm(true)'], 'E928:')
CheckDefExecFailure(['echo getfperm(v:null)'], 'E928:')
CheckDefExecFailure(['echo getfperm("")'], 'E928:')
enddef
def Test_getfsize()
assert_equal(-1, getfsize(""))
assert_equal(-1, getfsize(test_null_string()))
CheckDefExecFailure(['echo getfsize(true)'], 'E928:')
CheckDefExecFailure(['echo getfsize(v:null)'], 'E928:')
CheckDefExecFailure(['echo getfsize("")'], 'E928:')
enddef
def Test_getftime()
assert_equal(-1, getftime(""))
assert_equal(-1, getftime(test_null_string()))
CheckDefExecFailure(['echo getftime(true)'], 'E928:')
CheckDefExecFailure(['echo getftime(v:null)'], 'E928:')
CheckDefExecFailure(['echo getftime("")'], 'E928:')
enddef
def Test_getftype()
assert_equal('', getftype(""))
assert_equal('', getftype(test_null_string()))
CheckDefExecFailure(['echo getftype(true)'], 'E928:')
CheckDefExecFailure(['echo getftype(v:null)'], 'E928:')
CheckDefExecFailure(['echo getftype("")'], 'E928:')
enddef
def Test_getqflist_return_type()

View File

@@ -15,6 +15,7 @@ let t:tabpagevar = 't'
def s:ScriptFuncLoad(arg: string)
var local = 1
buffers
echo
echo arg
echo local
echo &lines
@@ -43,14 +44,27 @@ def Test_disassemble_load()
var res = execute('disass s:ScriptFuncLoad')
assert_match('<SNR>\d*_ScriptFuncLoad.*' ..
'buffers.*' ..
' EXEC \+buffers.*' ..
' LOAD arg\[-1\].*' ..
' LOAD $0.*' ..
' LOADOPT &lines.*' ..
' LOADV v:version.*' ..
' LOADS s:scriptvar from .*test_vim9_disassemble.vim.*' ..
' LOADG g:globalvar.*' ..
'buffers\_s*' ..
'\d\+ EXEC \+buffers\_s*' ..
'echo\_s*' ..
'echo arg\_s*' ..
'\d\+ LOAD arg\[-1\]\_s*' ..
'\d\+ ECHO 1\_s*' ..
'echo local\_s*' ..
'\d\+ LOAD $0\_s*' ..
'\d\+ ECHO 1\_s*' ..
'echo &lines\_s*' ..
'\d\+ LOADOPT &lines\_s*' ..
'\d\+ ECHO 1\_s*' ..
'echo v:version\_s*' ..
'\d\+ LOADV v:version\_s*' ..
'\d\+ ECHO 1\_s*' ..
'echo s:scriptvar\_s*' ..
'\d\+ LOADS s:scriptvar from .*test_vim9_disassemble.vim\_s*' ..
'\d\+ ECHO 1\_s*' ..
'echo g:globalvar\_s*' ..
'\d\+ LOADG g:globalvar\_s*' ..
'\d\+ ECHO 1\_s*' ..
'echo get(g:, "global")\_s*' ..
'\d\+ LOAD g:\_s*' ..
'\d\+ PUSHS "global"\_s*' ..

View File

@@ -481,6 +481,12 @@ def Test_call_lambda_args()
CheckDefFailure(lines, 'E1013: Argument 2: type mismatch, expected number but got string')
enddef
def Test_lambda_uses_assigned_var()
CheckDefSuccess([
'var x: any = "aaa"'
'x = filter(["bbb"], {_, v -> v =~ x})'])
enddef
" Default arg and varargs
def MyDefVarargs(one: string, two = 'foo', ...rest: list<string>): string
var res = one .. ',' .. two
@@ -1818,6 +1824,26 @@ def Test_reset_did_emsg()
delfunc! g:Func
enddef
def Test_did_emsg_reset()
# executing an autocommand resets did_emsg, this should not result in a
# builtin function considered failing
var lines =<< trim END
vim9script
au BufWinLeave * #
def Func()
popup_menu('', {callback: {-> popup_create('', {})->popup_close()}})
eval [][0]
enddef
nno <F3> <cmd>call <sid>Func()<cr>
feedkeys("\<F3>\e", 'xt')
END
writefile(lines, 'XemsgReset')
assert_fails('so XemsgReset', ['E684:', 'E684:'], lines, 2)
delete('XemsgReset')
nunmap <F3>
au! BufWinLeave
enddef
def Test_abort_with_silent_call()
var lines =<< trim END
vim9script

View File

@@ -620,7 +620,7 @@ def Test_try_catch_fails()
CheckDefFailure(['if 1', 'endtry'], 'E171:')
CheckDefFailure(['try', 'echo 1', 'endtry'], 'E1032:')
CheckDefFailure(['throw'], 'E1015:')
CheckDefFailure(['throw'], 'E1143:')
CheckDefFailure(['throw xxx'], 'E1001:')
enddef
@@ -1719,6 +1719,10 @@ def Test_nested_if()
enddef
def Test_execute_cmd()
# missing argument is ignored
execute
execute # comment
new
setline(1, 'default')
execute 'setline(1, "execute-string")'
@@ -2069,7 +2073,21 @@ def Test_vim9_comment()
CheckScriptSuccess([
'vim9script',
'# something',
'#something',
'#{something',
])
split Xfile
CheckScriptSuccess([
'vim9script',
'edit #something',
])
CheckScriptSuccess([
'vim9script',
'edit #{something',
])
close
CheckScriptFailure([
'vim9script',
':# something',
@@ -2123,9 +2141,6 @@ def Test_vim9_comment()
'vim9script',
'exe "echo"# something',
], 'E121:')
CheckDefFailure([
'exe # comment',
], 'E1015:')
CheckScriptFailure([
'vim9script',
'exe# something',
@@ -2150,7 +2165,7 @@ def Test_vim9_comment()
' throw#comment',
'catch',
'endtry',
], 'E1015:')
], 'E1143:')
CheckDefFailure([
'try',
' throw "yes"#comment',
@@ -3043,7 +3058,7 @@ def Test_put_with_linebreak()
new
var lines =<< trim END
vim9script
pu=split('abc', '\zs')
pu =split('abc', '\zs')
->join()
END
CheckScriptSuccess(lines)
@@ -3064,6 +3079,13 @@ def Test_invoke_normal_in_visual_mode()
xunmap <F3>
enddef
def Test_white_space_after_command()
var lines =<< trim END
exit_cb: Func})
END
CheckDefAndScriptFailure(lines, 'E1144:', 1)
enddef
" Keep this last, it messes up highlighting.
def Test_substitute_cmd()
new

View File

@@ -341,14 +341,12 @@ tv_get_float(typval_T *varp)
#endif
/*
* Give an error and return FAIL unless "tv" is a non-empty string.
* Give an error and return FAIL unless "tv" is a string.
*/
int
check_for_string(typval_T *tv)
{
if (tv->v_type != VAR_STRING
|| tv->vval.v_string == NULL
|| *tv->vval.v_string == NUL)
if (tv->v_type != VAR_STRING)
{
emsg(_(e_stringreq));
return FAIL;
@@ -356,6 +354,22 @@ check_for_string(typval_T *tv)
return OK;
}
/*
* Give an error and return FAIL unless "tv" is a non-empty string.
*/
int
check_for_nonempty_string(typval_T *tv)
{
if (check_for_string(tv) == FAIL)
return FAIL;
if (tv->vval.v_string == NULL || *tv->vval.v_string == NUL)
{
emsg(_(e_non_empty_string_required));
return FAIL;
}
return OK;
}
/*
* Get the string value of a variable.
* If it is a Number variable, the number is converted into a string.

View File

@@ -3185,7 +3185,9 @@ define_function(exarg_T *eap, char_u *name_arg)
lines_left = Rows - 1;
if (theline == NULL)
{
if (eap->cmdidx == CMD_def)
if (skip_until != NULL)
semsg(_(e_missing_heredoc_end_marker_str), skip_until);
else if (eap->cmdidx == CMD_def)
emsg(_(e_missing_enddef));
else
emsg(_("E126: Missing :endfunction"));
@@ -3352,18 +3354,24 @@ define_function(exarg_T *eap, char_u *name_arg)
// Check for ":cmd v =<< [trim] EOF"
// and ":cmd [a, b] =<< [trim] EOF"
// and "lines =<< [trim] EOF" for Vim9
// Where "cmd" can be "let", "var", "final" or "const".
arg = skipwhite(skiptowhite(p));
if (*arg == '[')
arg = vim_strchr(arg, ']');
if (arg != NULL)
{
arg = skipwhite(skiptowhite(arg));
if (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
int found = (eap->cmdidx == CMD_def && arg[0] == '='
&& arg[1] == '<' && arg[2] =='<');
if (!found)
// skip over the argument after "cmd"
arg = skipwhite(skiptowhite(arg));
if (found || (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
&& (checkforcmd(&p, "let", 2)
|| checkforcmd(&p, "var", 3)
|| checkforcmd(&p, "final", 5)
|| checkforcmd(&p, "const", 5)))
|| checkforcmd(&p, "const", 5))))
{
p = skipwhite(arg + 3);
if (STRNCMP(p, "trim", 4) == 0)

View File

@@ -750,6 +750,26 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2139,
/**/
2138,
/**/
2137,
/**/
2136,
/**/
2135,
/**/
2134,
/**/
2133,
/**/
2132,
/**/
2131,
/**/
2130,
/**/
2129,
/**/

View File

@@ -148,45 +148,51 @@ struct cctx_S {
static void delete_def_function_contents(dfunc_T *dfunc);
/*
* Lookup variable "name" in the local scope and return it.
* Return NULL if not found.
* Lookup variable "name" in the local scope and return it in "lvar".
* "lvar->lv_from_outer" is set accordingly.
* If "lvar" is NULL only check if the variable can be found.
* Return FAIL if not found.
*/
static lvar_T *
lookup_local(char_u *name, size_t len, cctx_T *cctx)
static int
lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
{
int idx;
lvar_T *lvar;
lvar_T *lvp;
if (len == 0)
return NULL;
return FAIL;
// Find local in current function scope.
for (idx = 0; idx < cctx->ctx_locals.ga_len; ++idx)
{
lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
if (STRNCMP(name, lvar->lv_name, len) == 0
&& STRLEN(lvar->lv_name) == len)
lvp = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
if (STRNCMP(name, lvp->lv_name, len) == 0
&& STRLEN(lvp->lv_name) == len)
{
lvar->lv_from_outer = FALSE;
return lvar;
if (lvar != NULL)
{
*lvar = *lvp;
lvar->lv_from_outer = FALSE;
}
return OK;
}
}
// Find local in outer function scope.
if (cctx->ctx_outer != NULL)
{
lvar = lookup_local(name, len, cctx->ctx_outer);
if (lvar != NULL)
if (lookup_local(name, len, lvar, cctx->ctx_outer) == OK)
{
// TODO: are there situations we should not mark the outer scope as
// used?
cctx->ctx_outer_used = TRUE;
lvar->lv_from_outer = TRUE;
return lvar;
if (lvar != NULL)
{
cctx->ctx_outer_used = TRUE;
lvar->lv_from_outer = TRUE;
}
return OK;
}
}
return NULL;
return FAIL;
}
/*
@@ -377,7 +383,7 @@ check_defined(char_u *p, size_t len, cctx_T *cctx)
p[len] = NUL;
if (script_var_exists(p, len, FALSE, cctx) == OK
|| (cctx != NULL
&& (lookup_local(p, len, cctx) != NULL
&& (lookup_local(p, len, NULL, cctx) == OK
|| arg_exists(p, len, NULL, NULL, NULL, cctx) == OK))
|| find_imported(p, len, cctx) != NULL
|| (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL)
@@ -2555,13 +2561,13 @@ compile_load(
}
else
{
lvar_T *lvar = lookup_local(*arg, len, cctx);
lvar_T lvar;
if (lvar != NULL)
if (lookup_local(*arg, len, &lvar, cctx) == OK)
{
type = lvar->lv_type;
idx = lvar->lv_idx;
if (lvar->lv_from_outer)
type = lvar.lv_type;
idx = lvar.lv_idx;
if (lvar.lv_from_outer)
gen_load_outer = TRUE;
else
gen_load = TRUE;
@@ -2763,7 +2769,7 @@ compile_call(
// An argument or local variable can be a function reference, this
// overrules a function name.
if (lookup_local(namebuf, varlen, cctx) == NULL
if (lookup_local(namebuf, varlen, NULL, cctx) == FAIL
&& arg_exists(namebuf, varlen, NULL, NULL, NULL, cctx) != OK)
{
// If we can find the function by name generate the right call.
@@ -3969,7 +3975,10 @@ compile_expr7(
if (!eval_isnamec1(**arg))
{
semsg(_(e_name_expected), *arg);
if (ends_excmd(*skipwhite(*arg)))
semsg(_(e_empty_expression_str), *arg);
else
semsg(_(e_name_expected_str), *arg);
return FAIL;
}
@@ -5366,6 +5375,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
assign_dest_T dest = dest_local;
int opt_flags = 0;
int vimvaridx = -1;
lvar_T local_lvar;
lvar_T *lvar = NULL;
lvar_T arg_lvar;
int has_type = FALSE;
@@ -5424,8 +5434,10 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
goto theend;
}
lvar = lookup_local(var_start, varlen, cctx);
if (lvar == NULL)
if (lookup_local(var_start, varlen, &local_lvar, cctx) == OK)
lvar = &local_lvar;
else
{
CLEAR_FIELD(arg_lvar);
if (arg_exists(var_start, varlen,
@@ -5844,8 +5856,6 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
int r;
// Compile the "idx" in "var[idx]" or "key" in "var.key".
if (new_local)
--cctx->ctx_locals.ga_len;
p = var_start + varlen;
if (*p == '[')
{
@@ -5865,8 +5875,6 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
r = generate_PUSHS(cctx, key);
}
if (new_local)
++cctx->ctx_locals.ga_len;
if (r == FAIL)
goto theend;
@@ -6579,8 +6587,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
}
else
{
var_lvar = lookup_local(arg, varlen, cctx);
if (var_lvar != NULL)
if (lookup_local(arg, varlen, NULL, cctx) == OK)
{
semsg(_(e_variable_already_declared), arg);
goto failed;
@@ -7093,28 +7100,31 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED)
compile_mult_expr(char_u *arg, int cmdidx, cctx_T *cctx)
{
char_u *p = arg;
char_u *prev;
char_u *prev = arg;
int count = 0;
for (;;)
{
if (ends_excmd2(prev, p))
break;
if (compile_expr0(&p, cctx) == FAIL)
return NULL;
++count;
prev = p;
p = skipwhite(p);
if (ends_excmd2(prev, p))
break;
}
if (cmdidx == CMD_echo || cmdidx == CMD_echon)
generate_ECHO(cctx, cmdidx == CMD_echo, count);
else if (cmdidx == CMD_execute)
generate_MULT_EXPR(cctx, ISN_EXECUTE, count);
else if (cmdidx == CMD_echomsg)
generate_MULT_EXPR(cctx, ISN_ECHOMSG, count);
else
generate_MULT_EXPR(cctx, ISN_ECHOERR, count);
if (count > 0)
{
if (cmdidx == CMD_echo || cmdidx == CMD_echon)
generate_ECHO(cctx, cmdidx == CMD_echo, count);
else if (cmdidx == CMD_execute)
generate_MULT_EXPR(cctx, ISN_EXECUTE, count);
else if (cmdidx == CMD_echomsg)
generate_MULT_EXPR(cctx, ISN_ECHOMSG, count);
else
generate_MULT_EXPR(cctx, ISN_ECHOERR, count);
}
return p;
}
@@ -7584,7 +7594,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
|| *ea.cmd == '$'
|| *ea.cmd == '@'
|| ((len) > 2 && ea.cmd[1] == ':')
|| lookup_local(ea.cmd, len, &cctx) != NULL
|| lookup_local(ea.cmd, len, NULL, &cctx) == OK
|| arg_exists(ea.cmd, len, NULL, NULL,
NULL, &cctx) == OK
|| script_var_exists(ea.cmd, len,
@@ -7637,7 +7647,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
}
}
p = find_ex_command(&ea, NULL, starts_with_colon ? NULL
: (void *(*)(char_u *, size_t, cctx_T *))lookup_local,
: (int (*)(char_u *, size_t, void *, cctx_T *))lookup_local,
&cctx);
if (p == ea.cmd && ea.cmdidx != CMD_SIZE)

View File

@@ -583,7 +583,7 @@ call_bfunc(int func_idx, int argcount, ectx_T *ectx)
for (idx = 0; idx < argcount; ++idx)
clear_tv(&argvars[idx]);
if (did_emsg != did_emsg_before)
if (did_emsg > did_emsg_before)
return FAIL;
return OK;
}