diff --git a/Makefile b/Makefile index e377392b52..86c9f65f19 100644 --- a/Makefile +++ b/Makefile @@ -152,7 +152,7 @@ MINOR = 2 # > cd src # > msvc2015.bat # - Build the console binary: -# > nmake -f Mae_mvc.mak +# > nmake -f Make_mvc.mak # - Run the tests and check the output: # > nmake -f Make_mvc.mak testclean # > nmake -f Make_mvc.mak test diff --git a/ci/appveyor.bat b/ci/appveyor.bat index 164d7bcc25..5d5a9f182f 100644 --- a/ci/appveyor.bat +++ b/ci/appveyor.bat @@ -11,7 +11,11 @@ sed -e "s/@<<$/@<< | sed -e 's#.*\\\\r.*##'/" Make_mvc.mak > Make_mvc2.mak echo "Building MSVC 64bit console Version" nmake -f Make_mvc2.mak CPU=AMD64 ^ OLE=no GUI=no IME=yes ICONV=yes DEBUG=no ^ - FEATURES=%FEATURE% || exit 1 + FEATURES=%FEATURE% +if not exist vim.exe ( + echo Build failure. + exit 1 +) :: build MSVC huge version with python and channel support :: GUI needs to be last, so that testing works @@ -21,16 +25,20 @@ if "%FEATURE%" == "HUGE" ( OLE=no GUI=yes IME=yes ICONV=yes DEBUG=no POSTSCRIPT=yes ^ PYTHON_VER=27 DYNAMIC_PYTHON=yes PYTHON=C:\Python27-x64 ^ PYTHON3_VER=35 DYNAMIC_PYTHON3=yes PYTHON3=C:\Python35-x64 ^ - FEATURES=%FEATURE% || exit 1 + FEATURES=%FEATURE% ) ELSE ( nmake -f Make_mvc2.mak CPU=AMD64 ^ OLE=no GUI=yes IME=yes ICONV=yes DEBUG=no ^ - FEATURES=%FEATURE% || exit 1 + FEATURES=%FEATURE% ) -.\gvim -u NONE -c "redir @a | ver |0put a | wq" ver_msvc.txt +if not exist gvim.exe ( + echo Build failure. + exit 1 +) +.\gvim -u NONE -c "redir @a | ver |0put a | wq" ver_msvc.txt || exit 1 echo "version output MSVC console" -.\vim --version +.\vim --version || exit 1 echo "version output MSVC GUI" -type ver_msvc.txt +type ver_msvc.txt || exit 1 cd .. diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index cacd39b9cc..947ebcb7df 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1037,7 +1037,7 @@ giving the mapping. Defaults without a .vimrc file ~ - *defaults.vim* + *defaults.vim* *E1187* If Vim is started normally and no user vimrc file is found, the $VIMRUNTIME/defaults.vim script is loaded. This will set 'compatible' off, switch on syntax highlighting and a few more things. See the script for diff --git a/src/drawline.c b/src/drawline.c index 3b37a23836..58922d557f 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1981,6 +1981,12 @@ win_line( // TODO: is passing p for start of the line OK? n_extra = win_lbr_chartabsize(wp, line, p, (colnr_T)vcol, NULL) - 1; + + // We have just drawn the showbreak value, no need to add + // space for it again + if (vcol == vcol_sbr) + n_extra -= MB_CHARLEN(get_showbreak_value(wp)); + if (c == TAB && n_extra + col > wp->w_width) # ifdef FEAT_VARTABS n_extra = tabstop_padding(vcol, wp->w_buffer->b_p_ts, diff --git a/src/errors.h b/src/errors.h index 2a0a536c84..d6e5dd1d26 100644 --- a/src/errors.h +++ b/src/errors.h @@ -413,3 +413,5 @@ EXTERN char e_missing_redir_end[] INIT(= N_("E1185: Missing :redir END")); EXTERN char e_expression_does_not_result_in_value_str[] INIT(= N_("E1186: Expression does not result in a value: %s")); +EXTERN char e_failed_to_source_defaults[] + INIT(= N_("E1187: Failed to source defaults.vim")); diff --git a/src/eval.c b/src/eval.c index b5820b8d32..8e55435496 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1309,6 +1309,9 @@ set_var_lval( { cc = *endp; *endp = NUL; + if (in_vim9script() && check_reserved_name(lp->ll_name) == FAIL) + return; + if (lp->ll_blob != NULL) { int error = FALSE, val; @@ -2358,7 +2361,7 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) ++*arg; if (evaluate && vim9script && !IS_WHITE_OR_NUL((*arg)[1])) { - error_white_both(p, op_falsy ? 2 : 1); + error_white_both(*arg - (op_falsy ? 1 : 0), op_falsy ? 2 : 1); clear_tv(rettv); return FAIL; } @@ -2406,7 +2409,7 @@ eval1(char_u **arg, typval_T *rettv, evalarg_T *evalarg) */ if (evaluate && vim9script && !IS_WHITE_OR_NUL((*arg)[1])) { - error_white_both(p, 1); + error_white_both(*arg, 1); clear_tv(rettv); evalarg_used->eval_flags = orig_flags; return FAIL; @@ -2511,7 +2514,7 @@ eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg) */ if (evaluate && in_vim9script() && !IS_WHITE_OR_NUL((*arg)[2])) { - error_white_both(p, 2); + error_white_both(*arg, 2); clear_tv(rettv); return FAIL; } @@ -2637,7 +2640,7 @@ eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg) */ if (evaluate && in_vim9script() && !IS_WHITE_OR_NUL((*arg)[2])) { - error_white_both(p, 2); + error_white_both(*arg, 2); clear_tv(rettv); return FAIL; } @@ -2735,10 +2738,13 @@ eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg) ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); if (getnext) + { *arg = eval_next_line(evalarg); + p = *arg; + } else if (evaluate && vim9script && !VIM_ISWHITE(**arg)) { - error_white_both(p, len); + error_white_both(*arg, len); clear_tv(rettv); return FAIL; } @@ -2898,7 +2904,7 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) { if (evaluate && vim9script && !VIM_ISWHITE(**arg)) { - error_white_both(p, oplen); + error_white_both(*arg, oplen); clear_tv(rettv); return FAIL; } @@ -2934,7 +2940,7 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) */ if (evaluate && vim9script && !IS_WHITE_OR_NUL((*arg)[oplen])) { - error_white_both(p, oplen); + error_white_both(*arg, oplen); clear_tv(rettv); return FAIL; } @@ -3130,7 +3136,7 @@ eval6( { if (evaluate && in_vim9script() && !VIM_ISWHITE(**arg)) { - error_white_both(p, 1); + error_white_both(*arg, 1); clear_tv(rettv); return FAIL; } diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 08372eae6d..1875b0446a 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3398,8 +3398,11 @@ find_ex_command( int len; char_u *p; int i; +#ifndef FEAT_EVAL + int vim9 = FALSE; +#else + int vim9 = in_vim9script(); -#ifdef FEAT_EVAL /* * Recognize a Vim9 script function/method call and assignment: * "lvar = value", "lvar(arg)", "[1, 2 3]->Func()" @@ -3562,12 +3565,13 @@ find_ex_command( * - the "d" command can directly be followed by 'l' or 'p' flag. */ p = eap->cmd; - if (*p == 'k') + if (!vim9 && *p == 'k') { eap->cmdidx = CMD_k; ++p; } - else if (p[0] == 's' + else if (!vim9 + && p[0] == 's' && ((p[1] == 'c' && (p[2] == NUL || (p[2] != 's' && p[2] != 'r' && (p[3] == NUL || (p[3] != 'i' && p[4] != 'p'))))) || p[1] == 'g' @@ -3600,7 +3604,7 @@ find_ex_command( if (p == eap->cmd && vim_strchr((char_u *)"@*!=><&~#}", *p) != NULL) ++p; len = (int)(p - eap->cmd); - if (*eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) + if (!vim9 && *eap->cmd == 'd' && (p[-1] == 'l' || p[-1] == 'p')) { // Check for ":dl", ":dell", etc. to ":deletel": that's // :delete with the 'l' flag. Same for 'p'. @@ -3677,7 +3681,7 @@ find_ex_command( #ifdef FEAT_EVAL if (eap->cmdidx < CMD_SIZE - && in_vim9script() + && vim9 && !IS_WHITE_OR_NUL(*p) && *p != '\n' && *p != '!' && (eap->cmdidx < 0 || (cmdnames[eap->cmdidx].cmd_argt & EX_NONWHITE_OK) == 0)) @@ -3797,17 +3801,32 @@ f_fullcommand(typval_T *argvars, typval_T *rettv) char_u *name = argvars[0].vval.v_string; char_u *p; - while (name[0] != NUL && name[0] == ':') + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (name == NULL) + return; + + while (*name != NUL && *name == ':') name++; name = skip_range(name, TRUE, NULL); - rettv->v_type = VAR_STRING; - ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name; ea.cmdidx = (cmdidx_T)0; + ea.addr_count = 0; p = find_ex_command(&ea, NULL, NULL, NULL); if (p == NULL || ea.cmdidx == CMD_SIZE) return; + if (in_vim9script()) + { + int res; + + ++emsg_silent; + res = not_in_vim9(&ea); + --emsg_silent; + + if (res == FAIL) + return; + } rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx) ? get_user_commands(NULL, ea.useridx) @@ -5485,7 +5504,7 @@ not_exiting(void) settmode(TMODE_RAW); } - static int + int before_quit_autocmds(win_T *wp, int quit_all, int forceit) { apply_autocmds(EVENT_QUITPRE, NULL, NULL, FALSE, wp->w_buffer); @@ -5559,7 +5578,7 @@ ex_quit(exarg_T *eap) #endif /* - * If there are more files or windows we won't exit. + * If there is only one relevant window we will exit. */ if (check_more(FALSE, eap->forceit) == OK && only_one_window()) exiting = TRUE; @@ -6076,7 +6095,7 @@ ex_stop(exarg_T *eap) } /* - * ":exit", ":xit" and ":wq": Write file and quite the current window. + * ":exit", ":xit" and ":wq": Write file and quit the current window. */ static void ex_exit(exarg_T *eap) @@ -6099,17 +6118,17 @@ ex_exit(exarg_T *eap) return; } - if (before_quit_autocmds(curwin, FALSE, eap->forceit)) - return; - /* - * if more files or windows we won't exit + * we plan to exit if there is only one relevant window */ if (check_more(FALSE, eap->forceit) == OK && only_one_window()) exiting = TRUE; - if ( ((eap->cmdidx == CMD_wq - || curbufIsChanged()) - && do_write(eap) == FAIL) + + // Write the buffer for ":wq" or when it was changed. + // Trigger QuitPre and ExitPre. + // Check if we can exit now, after autocommands have changed things. + if (((eap->cmdidx == CMD_wq || curbufIsChanged()) && do_write(eap) == FAIL) + || before_quit_autocmds(curwin, FALSE, eap->forceit) || check_more(TRUE, eap->forceit) == FAIL || (only_one_window() && check_changed_any(eap->forceit, FALSE))) { diff --git a/src/gui.c b/src/gui.c index 2b280e9c4a..7dcb4434df 100644 --- a/src/gui.c +++ b/src/gui.c @@ -866,9 +866,10 @@ gui_exit(int rc) void gui_shell_closed(void) { - cmdmod_T save_cmdmod; + cmdmod_T save_cmdmod = cmdmod; - save_cmdmod = cmdmod; + if (before_quit_autocmds(curwin, TRUE, FALSE)) + return; // Only exit when there are no changed files exiting = TRUE; diff --git a/src/if_perl.xs b/src/if_perl.xs index 999dff50ec..3b0fead5df 100644 --- a/src/if_perl.xs +++ b/src/if_perl.xs @@ -700,12 +700,41 @@ S_POPMARK(pTHX) /* perl-5.32 needs Perl_POPMARK */ # if (PERL_REVISION == 5) && (PERL_VERSION >= 32) # define Perl_POPMARK S_POPMARK +# endif + +/* perl-5.34 needs Perl_SvTRUE_common; used in SvTRUE_nomg_NN */ +# if (PERL_REVISION == 5) && (PERL_VERSION >= 34) +PERL_STATIC_INLINE bool +Perl_SvTRUE_common(pTHX_ SV * sv, const bool sv_2bool_is_fallback) +{ + if (UNLIKELY(SvIMMORTAL_INTERP(sv))) + return SvIMMORTAL_TRUE(sv); + + if (! SvOK(sv)) + return FALSE; + + if (SvPOK(sv)) + return SvPVXtrue(sv); + + if (SvIOK(sv)) + return SvIVX(sv) != 0; /* casts to bool */ + + if (SvROK(sv) && !(SvOBJECT(SvRV(sv)) && HvAMAGIC(SvSTASH(SvRV(sv))))) + return TRUE; + + if (sv_2bool_is_fallback) + return sv_2bool_nomg(sv); + + return isGV_with_GP(sv); +} +# endif /* perl-5.32 needs Perl_SvTRUE */ +# if (PERL_REVISION == 5) && (PERL_VERSION >= 32) PERL_STATIC_INLINE bool Perl_SvTRUE(pTHX_ SV *sv) { if (!LIKELY(sv)) - return FALSE; + return FALSE; SvGETMAGIC(sv); return SvTRUE_nomg_NN(sv); } diff --git a/src/main.c b/src/main.c index beb8950f90..9985e03453 100644 --- a/src/main.c +++ b/src/main.c @@ -3213,7 +3213,11 @@ source_startup_scripts(mparm_T *parmp) if (parmp->use_vimrc != NULL) { if (STRCMP(parmp->use_vimrc, "DEFAULTS") == 0) - do_source((char_u *)VIM_DEFAULTS_FILE, FALSE, DOSO_NONE, NULL); + { + if (do_source((char_u *)VIM_DEFAULTS_FILE, FALSE, DOSO_NONE, NULL) + != OK) + emsg(e_failed_to_source_defaults); + } else if (STRCMP(parmp->use_vimrc, "NONE") == 0 || STRCMP(parmp->use_vimrc, "NORC") == 0) { @@ -3285,7 +3289,9 @@ source_startup_scripts(mparm_T *parmp) && !has_dash_c_arg) { // When no .vimrc file was found: source defaults.vim. - do_source((char_u *)VIM_DEFAULTS_FILE, FALSE, DOSO_NONE, NULL); + if (do_source((char_u *)VIM_DEFAULTS_FILE, FALSE, DOSO_NONE, + NULL) == FAIL) + emsg(e_failed_to_source_defaults); } } diff --git a/src/memline.c b/src/memline.c index 4c4a1b24cb..5356369c16 100644 --- a/src/memline.c +++ b/src/memline.c @@ -2772,7 +2772,8 @@ ml_append_int( len = (colnr_T)STRLEN(line) + 1; // space needed for the text #ifdef FEAT_PROP_POPUP - if (curbuf->b_has_textprop && lnum > 0 && !(flags & ML_APPEND_UNDO)) + if (curbuf->b_has_textprop && lnum > 0 + && !(flags & (ML_APPEND_UNDO | ML_APPEND_NOPROP))) // Add text properties that continue from the previous line. add_text_props_for_append(buf, lnum, &line, &len, &tofree); #endif @@ -3992,7 +3993,11 @@ ml_flush_line(buf_T *buf) */ // How about handling errors??? (void)ml_append_int(buf, lnum, new_line, new_len, - (dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0); + ((dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0) +#ifdef FEAT_PROP_POPUP + | ML_APPEND_NOPROP +#endif + ); (void)ml_delete_int(buf, lnum, 0); } } diff --git a/src/message_test.c b/src/message_test.c index 88335de26c..882b591e43 100644 --- a/src/message_test.c +++ b/src/message_test.c @@ -120,6 +120,37 @@ test_trunc_string(void) vim_free(s); } +/* + * Test trunc_string() with mbyte chars. + */ + static void +test_trunc_string_mbyte(void) +{ + char_u *buf; // allocated every time to find uninit errors + char_u *s; + + buf = alloc(40); + s = vim_strsave((char_u *)"Ä text tha just fits"); + trunc_string(s, buf, 20, 40); + assert(STRCMP(buf, "Ä text tha just fits") == 0); + vim_free(buf); + vim_free(s); + + buf = alloc(40); + s = vim_strsave((char_u *)"a text ÄÖÜä nott fits"); + trunc_string(s, buf, 20, 40); + assert(STRCMP(buf, "a text Ä...nott fits") == 0); + vim_free(buf); + vim_free(s); + + buf = alloc(40); + s = vim_strsave((char_u *)"a text that not fitsÄ"); + trunc_string(s, buf, 20, 40); + assert(STRCMP(buf, "a text t...not fitsÄ") == 0); + vim_free(buf); + vim_free(s); +} + /* * Test vim_snprintf() with a focus on checking that truncation is * correct when buffer is small, since it cannot be tested from @@ -286,6 +317,7 @@ main(int argc, char **argv) set_option_value((char_u *)"encoding", 0, (char_u *)"utf-8", 0); init_chartab(); test_trunc_string(); + test_trunc_string_mbyte(); test_vim_snprintf(); set_option_value((char_u *)"encoding", 0, (char_u *)"latin1", 0); diff --git a/src/normal.c b/src/normal.c index 91fe2fcd3d..8c0482f21e 100644 --- a/src/normal.c +++ b/src/normal.c @@ -6156,6 +6156,17 @@ nv_g_cmd(cmdarg_T *cap) i = curwin->w_leftcol + curwin->w_width - col_off - 1; coladvance((colnr_T)i); + // if the character doesn't fit move one back + if (curwin->w_cursor.col > 0 + && (*mb_ptr2cells)(ml_get_cursor()) > 1) + { + colnr_T vcol; + + getvvcol(curwin, &curwin->w_cursor, NULL, NULL, &vcol); + if (vcol >= curwin->w_leftcol + curwin->w_width - col_off) + --curwin->w_cursor.col; + } + // Make sure we stick in this column. validate_virtcol(); curwin->w_curswant = curwin->w_virtcol; diff --git a/src/popupwin.c b/src/popupwin.c index 335345f3a0..35c4b0af5e 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -3822,17 +3822,29 @@ update_popups(void (*win_update)(win_T *wp)) title_wincol = wp->w_wincol + 1; if (wp->w_popup_title != NULL) { - char_u *title_text; + title_len = (int)MB_CHARLEN(wp->w_popup_title); - title_len = (int)STRLEN(wp->w_popup_title); - title_text = alloc(title_len + 1); - trunc_string(wp->w_popup_title, title_text, - total_width - 2, title_len + 1); - screen_puts(title_text, wp->w_winrow, title_wincol, - wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr); - vim_free(title_text); + // truncate the title if too long if (title_len > total_width - 2) + { + int title_byte_len = (int)STRLEN(wp->w_popup_title); + char_u *title_text = alloc(title_byte_len + 1); + + if (title_text != NULL) + { + trunc_string(wp->w_popup_title, title_text, + total_width - 2, title_byte_len + 1); + screen_puts(title_text, wp->w_winrow, title_wincol, + wp->w_popup_border[0] > 0 + ? border_attr[0] : popup_attr); + vim_free(title_text); + } + title_len = total_width - 2; + } + else + screen_puts(wp->w_popup_title, wp->w_winrow, title_wincol, + wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr); } wincol = wp->w_wincol - wp->w_popup_leftoff; diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index 14752d2d54..be7f24d1e1 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -7,13 +7,13 @@ 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); +int checkforcmd(char_u **pp, char *cmd, int len); int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, int skip_only); int has_cmdmod(cmdmod_T *cmod); int cmdmod_error(void); void apply_cmdmod(cmdmod_T *cmod); 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, int (*lookup)(char_u *, size_t, int cmd, cctx_T *), cctx_T *cctx); int modifier_len(char_u *cmd); @@ -33,6 +33,7 @@ char_u *find_nextcmd(char_u *p); char_u *check_nextcmd(char_u *p); char_u *get_command_name(expand_T *xp, int idx); void not_exiting(void); +int before_quit_autocmds(win_T *wp, int quit_all, int forceit); void ex_quit(exarg_T *eap); void tabpage_close(int forceit); void tabpage_close_other(tabpage_T *tp, int forceit); diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro index 2c4bd79606..41bf1287a1 100644 --- a/src/proto/vim9script.pro +++ b/src/proto/vim9script.pro @@ -18,4 +18,5 @@ void hide_script_var(scriptitem_T *si, int idx, int func_defined); void free_all_script_vars(scriptitem_T *si); svar_T *find_typval_in_script(typval_T *dest); int check_script_var_type(typval_T *dest, typval_T *value, char_u *name, where_T where); +int check_reserved_name(char_u *name); /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h index 93d89d0861..1081986114 100644 --- a/src/structs.h +++ b/src/structs.h @@ -774,6 +774,7 @@ typedef struct memline #define ML_APPEND_NEW 1 // starting to edit a new file #define ML_APPEND_MARK 2 // mark the new line #define ML_APPEND_UNDO 4 // called from undo +#define ML_APPEND_NOPROP 8 // do not continue textprop from previous line /* diff --git a/src/term.c b/src/term.c index 6f0e860d5f..8069b33738 100644 --- a/src/term.c +++ b/src/term.c @@ -4254,6 +4254,7 @@ add_termcode(char_u *name, char_u *string, int flags) if (new_tc == NULL) { tc_max_len -= 20; + vim_free(s); return; } for (i = 0; i < tc_len; ++i) diff --git a/src/testdir/Makefile b/src/testdir/Makefile index 050a032f1a..3f33bf8646 100644 --- a/src/testdir/Makefile +++ b/src/testdir/Makefile @@ -40,7 +40,7 @@ tiny: nolog tinytests report benchmark: $(SCRIPTS_BENCH) report: - # without the +eval feature test_result.log is a copy of test.log + @# without the +eval feature test_result.log is a copy of test.log @/bin/sh -c "if test -f test.log; \ then cp test.log test_result.log; \ else echo No failures reported > test_result.log; \ diff --git a/src/testdir/dumps/Test_popupwin_multibytetitle.dump b/src/testdir/dumps/Test_popupwin_multibytetitle.dump new file mode 100644 index 0000000000..e21c0cf2bb --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_multibytetitle.dump @@ -0,0 +1,10 @@ +>1+0&#ffffff0| @73 +|2| @73 +|3| @73 +|4| @25|╔+0#0000001#ffd7ff255|▶|Ä|Ö|Ü|◀|═@12|╗| +0#0000000#ffffff0@27 +|5| @25|║+0#0000001#ffd7ff255| |T+0&#e0e0e08|h|i|s| |i|s| |a| |l|i|n|e| @1| +0&#ffd7ff255|║| +0#0000000#ffffff0@27 +|6| @25|║+0#0000001#ffd7ff255| |a|n|d| |a|n|o|t|h|e|r| |l|i|n|e| |║| +0#0000000#ffffff0@27 +|7| @25|╚+0#0000001#ffd7ff255|═@17|╝| +0#0000000#ffffff0@27 +|8| @73 +|9| @73 +@57|1|,|1| @10|T|o|p| diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index d167d4c7c5..c3a638ba11 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -475,6 +475,7 @@ func Test_fullcommand() for [in, want] in items(tests) call assert_equal(want, fullcommand(in)) endfor + call assert_equal('', fullcommand(test_null_string())) call assert_equal('syntax', 'syn'->fullcommand()) endfunc diff --git a/src/testdir/test_cpoptions.vim b/src/testdir/test_cpoptions.vim index 02cadf4bbc..caf82ee80d 100644 --- a/src/testdir/test_cpoptions.vim +++ b/src/testdir/test_cpoptions.vim @@ -167,6 +167,7 @@ func Test_cpo_E() call assert_beeps('normal "ayl') " change an empty line call assert_beeps('normal lcTa') + call assert_beeps('normal 0c0') " delete an empty line call assert_beeps('normal D') call assert_beeps('normal dl') diff --git a/src/testdir/test_display.vim b/src/testdir/test_display.vim index 754046f2ae..964220cacd 100644 --- a/src/testdir/test_display.vim +++ b/src/testdir/test_display.vim @@ -334,4 +334,21 @@ func Test_fold_fillchars() set fillchars& fdc& foldmethod& foldenable& endfunc +func Test_display_linebreak_breakat() + new + vert resize 25 + let _breakat = &breakat + setl signcolumn=yes linebreak breakat=) showbreak=+\ + call setline(1, repeat('x', winwidth(0) - 2) .. ')abc') + let lines = ScreenLines([1, 2], 25) + let expected = [ + \ ' xxxxxxxxxxxxxxxxxxxxxxx', + \ ' + )abc ' + \ ] + call assert_equal(expected, lines) + %bw! + let &breakat=_breakat +endfunc + + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim index dde6e77ba7..597c022145 100644 --- a/src/testdir/test_expr.vim +++ b/src/testdir/test_expr.vim @@ -127,6 +127,7 @@ func Test_getreg_empty_list() let y = x call add(x, 'foo') call assert_equal(['foo'], y) + call assert_fails('call getreg([])', 'E730:') endfunc func Test_loop_over_null_list() diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index dcc2b04863..4337606584 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -141,6 +141,7 @@ func Test_min() call assert_fails('call min(1)', 'E712:') call assert_fails('call min(v:none)', 'E712:') + call assert_fails('call min([1, {}])', 'E728:') " check we only get one error call assert_fails('call min([[1], #{}])', ['E745:', 'E745:']) @@ -715,6 +716,7 @@ func Test_tr() call assert_fails("let s=tr('abcd', 'abcd', 'def')", 'E475:') call assert_equal('hEllO', tr('hello', 'eo', 'EO')) call assert_equal('hello', tr('hello', 'xy', 'ab')) + call assert_fails('call tr("abc", "123", "₁₂")', 'E475:') set encoding=utf8 endfunc @@ -2674,4 +2676,9 @@ func Test_default_arg_value() call assert_equal('msg', HasDefault()) endfunc +" Test for gettext() +func Test_gettext() + call assert_fails('call gettext(1)', 'E475:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_increment.vim b/src/testdir/test_increment.vim index 1a631ec00f..beacb0a44e 100644 --- a/src/testdir/test_increment.vim +++ b/src/testdir/test_increment.vim @@ -876,4 +876,21 @@ func Test_normal_increment_with_virtualedit() set virtualedit& endfunc +" Test for incrementing a signed hexadecimal and octal number +func Test_normal_increment_signed_hexoct_nr() + new + " negative sign before a hex number should be ignored + call setline(1, ["-0x9"]) + exe "norm \" + call assert_equal(["-0xa"], getline(1, '$')) + exe "norm \" + call assert_equal(["-0x9"], getline(1, '$')) + call setline(1, ["-007"]) + exe "norm \" + call assert_equal(["-010"], getline(1, '$')) + exe "norm \" + call assert_equal(["-007"], getline(1, '$')) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim index 601dd17e5e..46b030b86e 100644 --- a/src/testdir/test_listdict.vim +++ b/src/testdir/test_listdict.vim @@ -513,6 +513,11 @@ func Test_list_locked_var_unlet() call assert_equal(expected[depth][u][1], ps) endfor endfor + " Deleting a list range should fail if the range is locked + let l = [1, 2, 3, 4] + lockvar l[1:2] + call assert_fails('unlet l[1:2]', 'E741:') + unlet l endfunc " Locked variables and :unlet or list / dict functions diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim index 75b52bcb6e..f6e60a8ae5 100644 --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -1986,6 +1986,16 @@ func Test_normal30_changecase() call assert_equal(['aaaaaa', 'AAAAaa'], getline(1, 2)) set whichwrap& + " try changing the case with a double byte encoding (DBCS) + %bw! + let enc = &enc + set encoding=cp932 + call setline(1, "\u8470") + normal ~ + normal gU$gu$gUgUg~g~gugu + call assert_equal("\u8470", getline(1)) + let &encoding = enc + " clean up bw! endfunc @@ -2191,9 +2201,9 @@ func Test_normal33_g_cmd2() %d 15vsp set wrap listchars= sbr= - let lineA='abcdefghijklmnopqrstuvwxyz' - let lineB='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' - let lineC='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' + let lineA = 'abcdefghijklmnopqrstuvwxyz' + let lineB = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + let lineC = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' $put =lineA $put =lineB @@ -2228,6 +2238,28 @@ func Test_normal33_g_cmd2() call assert_equal('l', getreg(0)) call assert_beeps('normal 5g$') + " Test for g$ with double-width character half displayed + vsplit + 9wincmd | + setlocal nowrap nonumber + call setline(2, 'asdfasdfヨ') + 2 + normal 0g$ + call assert_equal(8, col('.')) + 10wincmd | + normal 0g$ + call assert_equal(9, col('.')) + + setlocal signcolumn=yes + 11wincmd | + normal 0g$ + call assert_equal(8, col('.')) + 12wincmd | + normal 0g$ + call assert_equal(9, col('.')) + + close + " Test for g_ call assert_beeps('normal! 100g_') call setline(2, [' foo ', ' foobar ']) @@ -3324,4 +3356,25 @@ func Test_normal_percent_jump() close! endfunc +" Test for << and >> commands to shift text by 'shiftwidth' +func Test_normal_shift_rightleft() + new + call setline(1, ['one', '', "\t", ' two', "\tthree", ' four']) + set shiftwidth=2 tabstop=8 + normal gg6>> + call assert_equal([' one', '', "\t ", ' two', "\t three", "\tfour"], + \ getline(1, '$')) + normal ggVG2>> + call assert_equal([' one', '', "\t ", "\ttwo", + \ "\t three", "\t four"], getline(1, '$')) + normal gg6<< + call assert_equal([' one', '', "\t ", ' two', "\t three", + \ "\t four"], getline(1, '$')) + normal ggVG2<< + call assert_equal(['one', '', "\t", ' two', "\tthree", ' four'], + \ getline(1, '$')) + set shiftwidth& tabstop& + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index f13252b055..13957e57c4 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -1799,6 +1799,11 @@ func Test_popup_title() call term_sendkeys(buf, ":\") call VerifyScreenDump(buf, 'Test_popupwin_longtitle_4', {}) + call term_sendkeys(buf, ":call popup_clear()\") + call term_sendkeys(buf, ":call popup_menu(['This is a line', 'and another line'], #{title: '▶ÄÖÜ◀', })\") + call VerifyScreenDump(buf, 'Test_popupwin_multibytetitle', {}) + call term_sendkeys(buf, "x") + " clean up call StopVimInTerminal(buf) call delete('XtestPopupTitle') diff --git a/src/testdir/test_registers.vim b/src/testdir/test_registers.vim index c10af29466..58f5191114 100644 --- a/src/testdir/test_registers.vim +++ b/src/testdir/test_registers.vim @@ -281,6 +281,7 @@ endfunc func Test_set_register() call assert_fails("call setreg('#', 200)", 'E86:') + call assert_fails("call setreg('a', test_unknown())", 'E908:') edit Xfile_alt_1 let b1 = bufnr('') @@ -470,6 +471,14 @@ func Test_get_reginfo() let info = getreginfo('"') call assert_equal('z', info.points_to) + let @a="a1b2" + nnoremap let g:RegInfo = getreginfo() + exe "normal \"a\" + call assert_equal({'regcontents': ['a1b2'], 'isunnamed': v:false, + \ 'regtype': 'v'}, g:RegInfo) + nunmap + unlet g:RegInfo + bwipe! endfunc diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim index 410553daf4..40c9a06937 100644 --- a/src/testdir/test_spellfile.vim +++ b/src/testdir/test_spellfile.vim @@ -583,6 +583,13 @@ func Test_mkspell() call assert_fails('mkspell! Xtest.spl Xtest.dic', 'E17:') call delete('Xtest.spl', 'rf') + " can't write the .spl file as its directory does not exist + call writefile([], 'Xtest.aff') + call writefile([], 'Xtest.dic') + call assert_fails('mkspell DOES_NOT_EXIT/Xtest.spl Xtest.dic', 'E484:') + call delete('Xtest.aff') + call delete('Xtest.dic') + call assert_fails('mkspell en en_US abc_xyz', 'E755:') endfunc @@ -842,6 +849,156 @@ func Test_spell_add_word() %bw! endfunc +func Test_spellfile_verbose() + call writefile(['1', 'one'], 'XtestVerbose.dic') + call writefile([], 'XtestVerbose.aff') + mkspell! XtestVerbose-utf8.spl XtestVerbose + set spell + + " First time: the spl file should be read. + let a = execute('3verbose set spelllang=XtestVerbose-utf8.spl') + call assert_match('Reading spell file "XtestVerbose-utf8.spl"', a) + + " Second time time: the spl file should not be read (already read). + let a = execute('3verbose set spelllang=XtestVerbose-utf8.spl') + call assert_notmatch('Reading spell file "XtestVerbose-utf8.spl"', a) + + set spell& spelllang& + call delete('XtestVerbose.dic') + call delete('XtestVerbose.aff') + call delete('XtestVerbose-utf8.spl') +endfunc + +" Test NOBREAK (see :help spell-NOBREAK) +func Test_NOBREAK() + call writefile(['3', 'one', 'two', 'three' ], 'XtestNOBREAK.dic') + call writefile(['NOBREAK' ], 'XtestNOBREAK.aff') + + mkspell! XtestNOBREAK-utf8.spl XtestNOBREAK + set spell spelllang=XtestNOBREAK-utf8.spl + + call assert_equal(['', ''], spellbadword('One two three onetwo onetwothree threetwoone')) + + call assert_equal(['x', 'bad'], spellbadword('x')) + call assert_equal(['y', 'bad'], spellbadword('yone')) + call assert_equal(['z', 'bad'], spellbadword('onez')) + call assert_equal(['zero', 'bad'], spellbadword('Onetwozerothree')) + + set spell& spelllang& + call delete('XtestNOBREAK.dic') + call delete('XtestNOBREAK.aff') + call delete('XtestNOBREAK-utf8.spl') +endfunc + +" Test CHECKCOMPOUNDPATTERN (see :help spell-CHECKCOMPOUNDPATTERN) +func Test_spellfile_CHECKCOMPOUNDPATTERN() + call writefile(['4', + \ 'one/c', + \ 'two/c', + \ 'three/c', + \ 'four'], 'XtestCHECKCOMPOUNDPATTERN.dic') + " Forbid compound words where first word ends with 'wo' and second starts with 'on'. + call writefile(['CHECKCOMPOUNDPATTERN 1', + \ 'CHECKCOMPOUNDPATTERN wo on', + \ 'COMPOUNDFLAG c'], 'XtestCHECKCOMPOUNDPATTERN.aff') + + mkspell! XtestCHECKCOMPOUNDPATTERN-utf8.spl XtestCHECKCOMPOUNDPATTERN + set spell spelllang=XtestCHECKCOMPOUNDPATTERN-utf8.spl + + " Check valid words with and without valid compounds. + for goodword in ['one', 'two', 'three', 'four', + \ 'oneone', 'onetwo', 'onethree', + \ 'twotwo', 'twothree', + \ 'threeone', 'threetwo', 'threethree', + \ 'onetwothree', 'onethreetwo', 'twothreeone', 'oneoneone'] + call assert_equal(['', ''], spellbadword(goodword), goodword) + endfor + + " Compounds 'twoone' or 'threetwoone' should be forbidden by CHECKCOMPOUNPATTERN. + " 'four' does not have the 'c' flag in *.aff file so no compound. + " 'five' is not in the *.dic file. + for badword in ['five', 'onetwox', + \ 'twoone', 'threetwoone', + \ 'fourone', 'onefour'] + call assert_equal([badword, 'bad'], spellbadword(badword)) + endfor + + set spell& spelllang& + call delete('XtestCHECKCOMPOUNDPATTERN.dic') + call delete('XtestCHECKCOMPOUNDPATTERN.aff') + call delete('XtestCHECKCOMPOUNDPATTERN-utf8.spl') +endfunc + +" Test COMMON (better suggestions with common words, see :help spell-COMMON) +func Test_spellfile_COMMON() + call writefile(['7', + \ 'and', + \ 'ant', + \ 'end', + \ 'any', + \ 'tee', + \ 'the', + \ 'ted'], 'XtestCOMMON.dic') + call writefile(['COMMON the and'], 'XtestCOMMON.aff') + + mkspell! XtestCOMMON-utf8.spl XtestCOMMON + set spell spelllang=XtestCOMMON-utf8.spl + + " COMMON words 'and' and 'the' should be the top suggestions. + call assert_equal(['and', 'ant'], spellsuggest('anr', 2)) + call assert_equal(['and', 'end'], spellsuggest('ond', 2)) + call assert_equal(['the', 'ted'], spellsuggest('tha', 2)) + call assert_equal(['the', 'tee'], spellsuggest('dhe', 2)) + + set spell& spelllang& + call delete('XtestCOMMON.dic') + call delete('XtestCOMMON.aff') + call delete('XtestCOMMON-utf8.spl') +endfunc + +" Test CIRCUMFIX (see: :help spell-CIRCUMFIX) +func Test_spellfile_CIRCUMFIX() + " Example taken verbatim from https://github.com/hunspell/hunspell/tree/master/tests + call writefile(['1', + \ 'nagy/C po:adj'], 'XtestCIRCUMFIX.dic') + call writefile(['# circumfixes: ~ obligate prefix/suffix combinations', + \ '# superlative in Hungarian: leg- (prefix) AND -bb (suffix)', + \ '', + \ 'CIRCUMFIX X', + \ '', + \ 'PFX A Y 1', + \ 'PFX A 0 leg/X .', + \ '', + \ 'PFX B Y 1', + \ 'PFX B 0 legesleg/X .', + \ '', + \ 'SFX C Y 3', + \ 'SFX C 0 obb . is:COMPARATIVE', + \ 'SFX C 0 obb/AX . is:SUPERLATIVE', + \ 'SFX C 0 obb/BX . is:SUPERSUPERLATIVE'], 'XtestCIRCUMFIX.aff') + + mkspell! XtestCIRCUMFIX-utf8.spl XtestCIRCUMFIX + set spell spelllang=XtestCIRCUMFIX-utf8.spl + + " From https://catalog.ldc.upenn.edu/docs/LDC2008T01/acta04.pdf: + " Hungarian English + " --------- ------- + " nagy great + " nagyobb greater + " legnagyobb greatest + " legeslegnagyob most greatest + call assert_equal(['', ''], spellbadword('nagy nagyobb legnagyobb legeslegnagyobb')) + + for badword in ['legnagy', 'legeslegnagy', 'legobb', 'legeslegobb'] + call assert_equal([badword, 'bad'], spellbadword(badword)) + endfor + + set spell& spelllang& + call delete('XtestCIRCUMFIX.dic') + call delete('XtestCIRCUMFIX.aff') + call delete('XtestCIRCUMFIX-utf8.spl') +endfunc + " When 'spellfile' is not set, adding a new good word will automatically set " the 'spellfile' func Test_init_spellfile() diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim index e75cbf0921..0d77101d4a 100644 --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -276,6 +276,20 @@ func Test_V_arg() call assert_match("sourcing \"$VIMRUNTIME[\\/]defaults\.vim\"\r\nline 1: \" The default vimrc file\..* verbose=15\n", out) endfunc +" Test that an error is shown when the defaults.vim file could not be read +" TODO: disabled - this causes ASAN errors for unknown reasons +"func Test_defaults_error() +" " Can't catch the output of gvim. +" CheckNotGui +" CheckNotMSWindows +" +" let out = system('VIMRUNTIME=/tmp ' .. GetVimCommand() .. ' --clean -cq') +" call assert_match("E1187: Failed to source defaults.vim", out) +" +" let out = system('VIMRUNTIME=/tmp ' .. GetVimCommand() .. ' -u DEFAULTS -cq') +" call assert_match("E1187: Failed to source defaults.vim", out) +"endfunc + " Test the '-q [errorfile]' argument. func Test_q_arg() CheckFeature quickfix diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index efa31f0a0a..d1e8f38d49 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -1469,5 +1469,24 @@ func Test_prop_one_line_window() bwipe! endfunc +" This was calling ml_append_int() and copy a text property from a previous +" line at the wrong moment. Exact text length matters. +def Test_prop_splits_data_block() + new + var lines: list = [repeat('x', 35)]->repeat(41) + + [repeat('!', 35)] + + [repeat('x', 35)]->repeat(56) + lines->setline(1) + prop_type_add('someprop', {highlight: 'ErrorMsg'}) + prop_add(1, 27, {end_lnum: 1, end_col: 70, type: 'someprop'}) + prop_remove({type: 'someprop'}, 1) + prop_add(35, 22, {end_lnum: 43, end_col: 43, type: 'someprop'}) + prop_remove({type: 'someprop'}, 35, 43) + assert_equal([], prop_list(42)) + + bwipe! + prop_type_delete('someprop') +enddef + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_user_func.vim b/src/testdir/test_user_func.vim index 4b8ec99a2b..747ec486bc 100644 --- a/src/testdir/test_user_func.vim +++ b/src/testdir/test_user_func.vim @@ -160,6 +160,16 @@ func Test_default_arg() \ .. "1 return deepcopy(a:)\n" \ .. " endfunction", \ execute('func Args2')) + + " Error in default argument expression + let l =<< trim END + func F1(x = y) + return a:x * 2 + endfunc + echo F1() + END + let @a = l->join("\n") + call assert_fails("exe @a", 'E121:') endfunc func s:addFoo(lead) diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index fa418b2ce2..b8c6433638 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -249,6 +249,13 @@ def Test_assignment() END enddef +def Test_reserved_name() + for name in ['true', 'false', 'null'] + CheckDefExecAndScriptFailure(['var ' .. name .. ' = 0'], 'E1034:') + CheckDefExecAndScriptFailure(['var ' .. name .. ': bool'], 'E1034:') + endfor +enddef + def Test_skipped_assignment() var lines =<< trim END for x in [] diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index c6df6fea18..3fa5eb3065 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -363,6 +363,7 @@ def Test_extend_arg_types() END CheckDefAndScriptSuccess(lines) + CheckDefFailure(['extend("a", 1)'], 'E1013: Argument 1: type mismatch, expected list but got string') CheckDefFailure(['extend([1, 2], 3)'], 'E1013: Argument 2: type mismatch, expected list but got number') CheckDefFailure(['extend([1, 2], ["x"])'], 'E1013: Argument 2: type mismatch, expected list but got list') CheckDefFailure(['extend([1, 2], [3], "x")'], 'E1013: Argument 3: type mismatch, expected number but got string') @@ -553,6 +554,29 @@ def Test_filter_missing_argument() res->assert_equal({aa: [1], ac: [3]}) enddef +def Test_fullcommand() + assert_equal('next', fullcommand('n')) + assert_equal('noremap', fullcommand('no')) + assert_equal('noremap', fullcommand('nor')) + assert_equal('normal', fullcommand('norm')) + + assert_equal('', fullcommand('k')) + assert_equal('keepmarks', fullcommand('ke')) + assert_equal('keepmarks', fullcommand('kee')) + assert_equal('keepmarks', fullcommand('keep')) + assert_equal('keepjumps', fullcommand('keepj')) + + assert_equal('dlist', fullcommand('dl')) + assert_equal('', fullcommand('dp')) + assert_equal('delete', fullcommand('del')) + assert_equal('', fullcommand('dell')) + assert_equal('', fullcommand('delp')) + + assert_equal('srewind', fullcommand('sre')) + assert_equal('scriptnames', fullcommand('scr')) + assert_equal('', fullcommand('scg')) +enddef + def Test_garbagecollect() garbagecollect(true) enddef @@ -726,6 +750,12 @@ def Test_insert() endfor res->assert_equal(6) + var m: any = [] + insert(m, 4) + call assert_equal([4], m) + extend(m, [6], 0) + call assert_equal([6, 4], m) + var lines =<< trim END insert(test_null_list(), 123) END @@ -743,6 +773,7 @@ def Test_insert() assert_equal(['a', 'b', 'c'], insert(['b', 'c'], 'a')) assert_equal(0z1234, insert(0z34, 0x12)) + CheckDefFailure(['insert("a", 1)'], 'E1013: Argument 1: type mismatch, expected list but got string', 1) CheckDefFailure(['insert([2, 3], "a")'], 'E1013: Argument 2: type mismatch, expected number but got string', 1) CheckDefFailure(['insert([2, 3], 1, "x")'], 'E1013: Argument 3: type mismatch, expected number but got string', 1) enddef diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 4a67522cb6..22186808e4 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -172,11 +172,23 @@ func Test_expr1_trinary_fails() call CheckDefAndScriptFailure(["var x = 1? 'one' : 'two'"], msg, 1) call CheckDefAndScriptFailure(["var x = 1 ?'one' : 'two'"], msg, 1) call CheckDefAndScriptFailure(["var x = 1?'one' : 'two'"], msg, 1) + let lines =<< trim END + var x = 1 + ?'one' : 'two' + # comment + END + call CheckDefAndScriptFailure(lines, 'E1004: White space required before and after ''?'' at "?''one'' : ''two''"', 2) let msg = "White space required before and after ':'" call CheckDefAndScriptFailure(["var x = 1 ? 'one': 'two'"], msg, 1) call CheckDefAndScriptFailure(["var x = 1 ? 'one' :'two'"], msg, 1) call CheckDefAndScriptFailure(["var x = 1 ? 'one':'two'"], msg, 1) + let lines =<< trim END + var x = 1 ? 'one' + :'two' + # Comment + END + call CheckDefAndScriptFailure(lines, 'E1004: White space required before and after '':'' at ":''two''"', 2) call CheckDefAndScriptFailure(["var x = 'x' ? 'one' : 'two'"], 'E1135:', 1) call CheckDefAndScriptFailure(["var x = 0z1234 ? 'one' : 'two'"], 'E974:', 1) @@ -229,6 +241,12 @@ def Test_expr1_falsy() call CheckDefAndScriptFailure(["var x = 1?? 'one' : 'two'"], msg, 1) call CheckDefAndScriptFailure(["var x = 1 ??'one' : 'two'"], msg, 1) call CheckDefAndScriptFailure(["var x = 1??'one' : 'two'"], msg, 1) + lines =<< trim END + var x = 1 + ??'one' : 'two' + #comment + END + CheckDefAndScriptFailure(lines, 'E1004: White space required before and after ''??'' at "??''one'' : ''two''"', 2) enddef def Record(val: any): any @@ -376,6 +394,13 @@ def Test_expr2_fails() call CheckDefAndScriptFailure2(["var x = [] || false"], 'E1012: Type mismatch; expected bool but got list', 'E745:', 1) + var lines =<< trim END + vim9script + echo false + ||true + # comment + END + CheckScriptFailure(lines, 'E1004: White space required before and after ''||'' at "||true"', 3) enddef " test && @@ -476,13 +501,19 @@ def Test_expr3_fails() CheckDefAndScriptFailure(["var x = 1&&2"], msg, 1) CheckDefAndScriptFailure(["var x = 1 &&2"], msg, 1) CheckDefAndScriptFailure(["var x = 1&& 2"], msg, 1) + var lines =<< trim END + var x = 1 + &&2 + # comment + END + CheckDefAndScriptFailure(lines, 'E1004: White space required before and after ''&&'' at "&&2"', 2) g:vals = [] CheckDefAndScriptFailure2(["if 'yes' && 0", 'echo 0', 'endif'], 'E1012: Type mismatch; expected bool but got string', 'E1135: Using a String as a Bool', 1) CheckDefExecAndScriptFailure(['assert_equal(false, Record(1) && Record(4) && Record(0))'], 'E1023: Using a Number as a Bool: 4', 1) - var lines =<< trim END + lines =<< trim END if 3 && true endif @@ -976,6 +1007,12 @@ def Test_expr4_vim9script() END CheckDefAndScriptFailure(lines, 'E1004:', 1) + for op in ['==', '>', '>=', '<', '<=', '=~', '!~', 'is', 'isnot'] + lines = ["echo 'aaa'", op .. "'bbb'", '# comment'] + var msg = printf("E1004: White space required before and after '%s'", op) + CheckDefAndScriptFailure(lines, msg, 2) + endfor + lines =<< trim END echo len('xxx') == 3 END @@ -1220,7 +1257,14 @@ def Test_expr5_vim9script() lines =<< trim END echo 'a'.. 'b' END - CheckDefAndScriptFailure(lines, 'E1004:', 1) + CheckDefAndScriptFailure(lines, 'E1004: White space required before and after ''..'' at ".. ''b''"', 1) + + lines =<< trim END + echo 'a' + ..'b' + # comment + END + CheckDefAndScriptFailure(lines, 'E1004: White space required before and after ''..'' at "..''b''"', 2) # check invalid string concatenation lines =<< trim END @@ -1257,6 +1301,12 @@ def Test_expr5_vim9script() bwipe! END CheckDefAndScriptFailure(lines, "E1004: White space required before and after '/' at \"/pattern", 3) + + for op in ['+', '-'] + lines = ['var x = 1', op .. '2', '# comment'] + var msg = printf("E1004: White space required before and after '%s' at \"%s2\"", op, op) + CheckDefAndScriptFailure(lines, msg, 2) + endfor enddef def Test_expr5_vim9script_channel() @@ -1538,6 +1588,12 @@ func Test_expr6_fails() if has('float') call CheckDefAndScriptFailure2(["var x = 0.7[1]"], 'E1107:', 'E806:', 1) endif + + for op in ['*', '/', '%'] + let lines = ['var x = 1', op .. '2', '# comment'] + let msg = printf("E1004: White space required before and after '%s' at \"%s2\"", op, op) + call CheckDefAndScriptFailure(lines, msg, 2) + endfor endfunc func Test_expr6_float_fails() @@ -1580,6 +1636,8 @@ def Test_expr7t() var ln: list = [g:anint, g:thefour] var nr = 234 assert_equal(234, nr) + var b: bool = 1 + assert_equal(true, b) var text = 'text' @@ -1591,6 +1649,7 @@ def Test_expr7t() CheckDefAndScriptFailure(["var x = 123"], 'E1010:', 1) CheckDefFailure(["var x = "], 'E1097:', 3) + CheckDefFailure(["var x = string(1)"], 'E1012:', 1) CheckScriptFailure(['vim9script', "var x = "], 'E15:', 2) CheckDefAndScriptFailure(["var x = 123"], 'E1068:', 1) CheckDefAndScriptFailure(["var x = )', 'echo "a"', 'enddef'], 'E1069:') + CheckScriptFailure(['def Func7(...x: int)', 'echo "a"', 'enddef'], 'E1010:') enddef def Test_white_space_before_comma() @@ -2717,6 +2720,11 @@ def Test_ignored_argument() var _ = 1 END CheckDefAndScriptFailure(lines, 'E1181:', 1) + + lines =<< trim END + var x = _ + END + CheckDefAndScriptFailure(lines, 'E1181:', 1) enddef def Test_too_many_arguments() diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 7114ae41e1..cdda613218 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -3844,12 +3844,14 @@ def Test_unsupported_commands() var lines =<< trim END ka END - CheckDefAndScriptFailure(lines, 'E1100:') + CheckDefFailure(lines, 'E476:') + CheckScriptFailure(['vim9script'] + lines, 'E492:') lines =<< trim END :1ka END - CheckDefAndScriptFailure(lines, 'E481:') + CheckDefFailure(lines, 'E476:') + CheckScriptFailure(['vim9script'] + lines, 'E492:') lines =<< trim END t diff --git a/src/testdir/test_virtualedit.vim b/src/testdir/test_virtualedit.vim index e1cd20e835..7f6e08c7c7 100644 --- a/src/testdir/test_virtualedit.vim +++ b/src/testdir/test_virtualedit.vim @@ -80,6 +80,10 @@ func Test_edit_change() call setline(1, "\t⒌") normal Cx call assert_equal('x', getline(1)) + " Do a visual block change + call setline(1, ['a', 'b', 'c']) + exe "normal gg3l\2jcx" + call assert_equal(['a x', 'b x', 'c x'], getline(1, '$')) bwipe! set virtualedit= endfunc @@ -289,6 +293,16 @@ func Test_replace_after_eol() call append(0, '"r"') normal gg$5lrxa call assert_equal('"r" x', getline(1)) + " visual block replace + %d _ + call setline(1, ['a', '', 'b']) + exe "normal 2l\2jrx" + call assert_equal(['a x', ' x', 'b x'], getline(1, '$')) + " visual characterwise selection replace after eol + %d _ + call setline(1, 'a') + normal 4lv2lrx + call assert_equal('a xxx', getline(1)) bwipe! set virtualedit= endfunc @@ -375,4 +389,17 @@ func Test_ve_backspace() close! endfunc +" Test for delete (x) on EOL character and after EOL +func Test_delete_past_eol() + new + call setline(1, "ab") + set virtualedit=all + exe "normal 2lx" + call assert_equal('ab', getline(1)) + exe "normal 10lx" + call assert_equal('ab', getline(1)) + set virtualedit& + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim index 7c5f973a0c..cd039c09fc 100644 --- a/src/testdir/test_visual.vim +++ b/src/testdir/test_visual.vim @@ -81,12 +81,11 @@ func Test_visual_mode_reset() " thus preventing the problem: exe "normal! GV:call TriggerTheProblem()\" call assert_equal("Everything's fine.", g:msg) - endfunc " Test for visual block shift and tab characters. func Test_block_shift_tab() - enew! + new call append(0, repeat(['one two three'], 5)) call cursor(1,1) exe "normal i\u" @@ -95,7 +94,7 @@ func Test_block_shift_tab() call assert_equal('on1 two three', getline(2)) call assert_equal('on1 two three', getline(5)) - enew! + %d _ call append(0, repeat(['abcdefghijklmnopqrstuvwxyz'], 5)) call cursor(1,1) exe "normal \4jI \j<<11|D" @@ -120,12 +119,26 @@ func Test_block_shift_tab() call assert_equal(" abc\\defghijklmnopqrstuvwxyz", getline(4)) call assert_equal(" abc\ defghijklmnopqrstuvwxyz", getline(5)) - enew! + " Test for block shift with space characters at the beginning and with + " 'noexpandtab' and 'expandtab' + %d _ + call setline(1, [" 1", " 2", " 3"]) + setlocal shiftwidth=2 noexpandtab + exe "normal gg\3j>" + call assert_equal(["\t1", "\t2", "\t3"], getline(1, '$')) + %d _ + call setline(1, [" 1", " 2", " 3"]) + setlocal shiftwidth=2 expandtab + exe "normal gg\3j>" + call assert_equal([" 1", " 2", " 3"], getline(1, '$')) + setlocal shiftwidth& + + bw! endfunc " Tests Blockwise Visual when there are TABs before the text. func Test_blockwise_visual() - enew! + new call append(0, ['123456', \ '234567', \ '345678', @@ -147,12 +160,12 @@ func Test_blockwise_visual() \ "\t\tsomext", \ "\t\ttesext"], getline(1, 7)) - enew! + bw! endfunc " Test swapping corners in blockwise visual mode with o and O func Test_blockwise_visual_o_O() - enew! + new exe "norm! 10i.\Y4P3lj\4l2jr " exe "norm! gvO\ra" @@ -171,7 +184,7 @@ func Test_blockwise_visual_o_O() \ '...a bf.', \ '..........'], getline(1, '$')) - enew! + bw! endfun " Test Virtual replace mode. @@ -459,15 +472,13 @@ endfunc " Test for 'p'ut in visual block mode func Test_visual_block_put() - enew - + new call append(0, ['One', 'Two', 'Three']) normal gg yank call feedkeys("jl\ljp", 'xt') call assert_equal(['One', 'T', 'Tee', 'One', ''], getline(1, '$')) - - enew! + bw! endfunc " Visual modes (v V CTRL-V) followed by an operator; count; repeating @@ -646,6 +657,12 @@ func Test_characterwise_visual_mode() norm! G1vy call assert_equal('four', @") + " characterwise visual mode: replace a single character line and the eol + %d _ + call setline(1, "a") + normal v$rx + call assert_equal(['x'], getline(1, '$')) + bwipe! endfunc @@ -741,6 +758,66 @@ func Test_visual_block_mode() exe "normal! \j2lD" call assert_equal(['ax', 'ax'], getline(3, 4)) + " Test block insert with a short line that ends before the block + %d _ + call setline(1, [" one", "a", " two"]) + exe "normal gg\2jIx" + call assert_equal([" xone", "a", " xtwo"], getline(1, '$')) + + " Test block append at EOL with '$' and without '$' + %d _ + call setline(1, ["one", "a", "two"]) + exe "normal gg$\2jAx" + call assert_equal(["onex", "ax", "twox"], getline(1, '$')) + %d _ + call setline(1, ["one", "a", "two"]) + exe "normal gg3l\2jAx" + call assert_equal(["onex", "a x", "twox"], getline(1, '$')) + + " Test block replace with an empty line in the middle and use $ to jump to + " the end of the line. + %d _ + call setline(1, ['one', '', 'two']) + exe "normal gg$\2jrx" + call assert_equal(["onx", "", "twx"], getline(1, '$')) + + " Test block replace with an empty line in the middle and move cursor to the + " end of the line + %d _ + call setline(1, ['one', '', 'two']) + exe "normal gg2l\2jrx" + call assert_equal(["onx", "", "twx"], getline(1, '$')) + + " Replace odd number of characters with a multibyte character + %d _ + call setline(1, ['abcd', 'efgh']) + exe "normal ggl\2ljr\u1100" + call assert_equal(["a\u1100 ", "e\u1100 "], getline(1, '$')) + + " During visual block append, if the cursor moved outside of the selected + " range, then the edit should not be applied to the block. + %d _ + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "normal 2G\jAx\" + call assert_equal(['aaa', 'bxbb', 'ccc'], getline(1, '$')) + + " During visual block append, if the cursor is moved before the start of the + " block, then the new text should be appended there. + %d _ + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "normal $\2jA\x" + " BUG: Instead of adding x as the third character in all the three lines, + " 'a' is added in the second and third lines at the end. This bug is not + " reproducible if this operation is performed manually. + "call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) + call assert_equal(['aaxa', 'bbba', 'ccca'], getline(1, '$')) + + " Change a characterwise motion to a blockwise motion using CTRL-V + %d _ + call setline(1, ['123', '456', '789']) + exe "normal ld\j" + call assert_equal(['13', '46', '789'], getline(1, '$')) + bwipe! endfunc diff --git a/src/testdir/test_writefile.vim b/src/testdir/test_writefile.vim index e4293e3f10..e94071fd7d 100644 --- a/src/testdir/test_writefile.vim +++ b/src/testdir/test_writefile.vim @@ -915,4 +915,25 @@ func Test_write_binary_file() call delete('Xfile3') endfunc +" Check that buffer is written before triggering QuitPre +func Test_wq_quitpre_autocommand() + edit Xsomefile + call setline(1, 'hello') + split + let g:seq = [] + augroup Testing + au QuitPre * call add(g:seq, 'QuitPre - ' .. (&modified ? 'modified' : 'not modified')) + au BufWritePost * call add(g:seq, 'written') + augroup END + wq + call assert_equal(['written', 'QuitPre - not modified'], g:seq) + + augroup Testing + au! + augroup END + bwipe! + unlet g:seq + call delete('Xsomefile') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/time.c b/src/time.c index c5ab693050..a28708f9f8 100644 --- a/src/time.c +++ b/src/time.c @@ -252,7 +252,6 @@ f_reltimestr(typval_T *argvars UNUSED, typval_T *rettv) void f_strftime(typval_T *argvars, typval_T *rettv) { - char_u result_buf[256]; struct tm tmval; struct tm *curtime; time_t seconds; @@ -271,6 +270,20 @@ f_strftime(typval_T *argvars, typval_T *rettv) rettv->vval.v_string = vim_strsave((char_u *)_("(Invalid)")); else { +# ifdef MSWIN + WCHAR result_buf[256]; + WCHAR *wp; + + wp = enc_to_utf16(p, NULL); + if (wp != NULL) + (void)wcsftime(result_buf, sizeof(result_buf) / sizeof(WCHAR), + wp, curtime); + else + result_buf[0] = NUL; + rettv->vval.v_string = utf16_to_enc(result_buf, NULL); + vim_free(wp); +# else + char_u result_buf[256]; vimconv_T conv; char_u *enc; @@ -296,6 +309,7 @@ f_strftime(typval_T *argvars, typval_T *rettv) // Release conversion descriptors convert_setup(&conv, NULL, NULL); vim_free(enc); +# endif } } # endif diff --git a/src/version.c b/src/version.c index b03dc56755..eefb08c810 100644 --- a/src/version.c +++ b/src/version.c @@ -765,6 +765,50 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2907, +/**/ + 2906, +/**/ + 2905, +/**/ + 2904, +/**/ + 2903, +/**/ + 2902, +/**/ + 2901, +/**/ + 2900, +/**/ + 2899, +/**/ + 2898, +/**/ + 2897, +/**/ + 2896, +/**/ + 2895, +/**/ + 2894, +/**/ + 2893, +/**/ + 2892, +/**/ + 2891, +/**/ + 2890, +/**/ + 2889, +/**/ + 2888, +/**/ + 2887, +/**/ + 2886, /**/ 2885, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index cb91c3ccb9..2ea487de78 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -5187,7 +5187,7 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) if (!IS_WHITE_OR_NUL(**arg) || !IS_WHITE_OR_NUL(p[1 + op_falsy])) { semsg(_(e_white_space_required_before_and_after_str_at_str), - op_falsy ? "??" : "?", *arg); + op_falsy ? "??" : "?", p); return FAIL; } @@ -5594,14 +5594,6 @@ assignment_len(char_u *p, int *heredoc) return 0; } -// words that cannot be used as a variable -static char *reserved[] = { - "true", - "false", - "null", - NULL -}; - /* * Generate the load instruction for "name". */ @@ -5995,16 +5987,9 @@ compile_lhs( } else { - int idx; - // No specific kind of variable recognized, just a name. - for (idx = 0; reserved[idx] != NULL; ++idx) - if (STRCMP(reserved[idx], lhs->lhs_name) == 0) - { - semsg(_(e_cannot_use_reserved_name), lhs->lhs_name); - return FAIL; - } - + if (check_reserved_name(lhs->lhs_name) == FAIL) + return FAIL; if (lookup_local(var_start, lhs->lhs_varlen, &lhs->lhs_local_lvar, cctx) == OK) diff --git a/src/vim9script.c b/src/vim9script.c index 02c04e2ea7..fbb815c28e 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -709,10 +709,10 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg) } name = vim_strnsave(arg, p - arg); - // parse type + // parse type, check for reserved name p = skipwhite(p + 1); type = parse_type(&p, &si->sn_type_list, TRUE); - if (type == NULL) + if (type == NULL || check_reserved_name(name) == FAIL) { vim_free(name); return p; @@ -974,4 +974,26 @@ check_script_var_type( return OK; // not really } +// words that cannot be used as a variable +static char *reserved[] = { + "true", + "false", + "null", + NULL +}; + + int +check_reserved_name(char_u *name) +{ + int idx; + + for (idx = 0; reserved[idx] != NULL; ++idx) + if (STRCMP(reserved[idx], name) == 0) + { + semsg(_(e_cannot_use_reserved_name), name); + return FAIL; + } + return OK; +} + #endif // FEAT_EVAL