From 0981c8729e09551f2e8e6c159bc29f2c1d04019c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Aug 2020 14:28:37 +0200 Subject: [PATCH 01/19] patch 8.2.1513: cannot interrupt shell used for filename expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Cannot interrupt shell used for filename expansion. (Dominique Pellé) Solution: Do set tmode in mch_delay(). (closes #6770) --- src/channel.c | 2 +- src/if_cscope.c | 4 ++-- src/os_amiga.c | 6 +++--- src/os_unix.c | 35 ++++++++++++++++++++++------------- src/os_win32.c | 4 ++-- src/proto/os_amiga.pro | 2 +- src/proto/os_unix.pro | 2 +- src/proto/os_win32.pro | 2 +- src/term.c | 2 +- src/ui.c | 2 +- src/version.c | 2 ++ src/vim.h | 4 ++++ 12 files changed, 41 insertions(+), 26 deletions(-) diff --git a/src/channel.c b/src/channel.c index 3e7ec96598..0b41133c2d 100644 --- a/src/channel.c +++ b/src/channel.c @@ -904,7 +904,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; } diff --git a/src/if_cscope.c b/src/if_cscope.c index 0e6f5ae30f..c027c71b48 100644 --- a/src/if_cscope.c +++ b/src/if_cscope.c @@ -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) diff --git a/src/os_amiga.c b/src/os_amiga.c index 850c26acda..91c13e7d29 100644 --- a/src/os_amiga.c +++ b/src/os_amiga.c @@ -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); diff --git a/src/os_unix.c b/src/os_unix.c index 0f61367f2a..73caa1a3e4 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -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; @@ -4170,7 +4173,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; @@ -5262,6 +5265,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. @@ -5295,11 +5301,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 @@ -6710,7 +6719,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; diff --git a/src/os_win32.c b/src/os_win32.c index 43ad1026d5..96af44364b 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -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) { diff --git a/src/proto/os_amiga.pro b/src/proto/os_amiga.pro index b2ad9c3b55..abebae154f 100644 --- a/src/proto/os_amiga.pro +++ b/src/proto/os_amiga.pro @@ -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); diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro index cb84994429..ee22b4a422 100644 --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -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); diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro index 85c09c0ba1..18c81273e1 100644 --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -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); diff --git a/src/term.c b/src/term.c index 3648df6d50..b1a7a66c58 100644 --- a/src/term.c +++ b/src/term.c @@ -3598,7 +3598,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. diff --git a/src/ui.c b/src/ui.c index e473f85259..fc24a01b4d 100644 --- a/src/ui.c +++ b/src/ui.c @@ -539,7 +539,7 @@ ui_delay(long msec_arg, int ignoreinput) gui_wait_for_chars(msec, typebuf.tb_change_cnt); else #endif - mch_delay(msec, ignoreinput); + mch_delay(msec, ignoreinput ? MCH_DELAY_IGNOREINPUT : 0); } /* diff --git a/src/version.c b/src/version.c index 2b996d6914..b52d4a8c62 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1513, /**/ 1512, /**/ diff --git a/src/vim.h b/src/vim.h index 2b28948726..7bcc488c81 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2668,4 +2668,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 From dc0cf1db3e8e0de349e4cc5b0a7ab6c8b7dc2ebb Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Aug 2020 15:09:36 +0200 Subject: [PATCH 02/19] patch 8.2.1514: multibyte vertical separator is cleared when dragging popup Problem: Multibyte vertical separator is cleared when dragging a popup window using a multi-byte character for the border. Solution: Only clear the character before the window if it is double width. (closes #6766) --- src/screen.c | 3 ++- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/screen.c b/src/screen.c index 86ebd400b0..233d6a5852 100644 --- a/src/screen.c +++ b/src/screen.c @@ -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; diff --git a/src/version.c b/src/version.c index b52d4a8c62..02457eb5e9 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1514, /**/ 1513, /**/ From 8436773fad285215481c4ce2b32692e66fca599f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Aug 2020 15:21:55 +0200 Subject: [PATCH 03/19] patch 8.2.1515: Vim9: can create s:var in legacy script but cannot unlet Problem: Vim9: can create s:var in legacy script but cannot unlet. Solution: Allow :unlet for legacy script var. --- src/testdir/test_vim9_script.vim | 7 +++++++ src/version.c | 2 ++ src/vim9compile.c | 15 +++++++++++++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 5a251473cd..05b4e9f022 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -608,6 +608,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', diff --git a/src/version.c b/src/version.c index 02457eb5e9..65b9d0fd72 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1515, /**/ 1514, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index e4b7d44adb..c0cea2992c 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -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; @@ -5234,6 +5242,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; } From 430deb1945cbc7a17ed42c5c737fc08d2eef327a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Aug 2020 16:29:11 +0200 Subject: [PATCH 04/19] patch 8.2.1516: Vim9: error for :exe has wrong line number Problem: Vim9: error for :exe has wrong line number. Solution: Set line number before calling do_cmdline_cmd(). (closes #6774) --- src/testdir/test_vim9_script.vim | 5 +++-- src/version.c | 2 ++ src/vim9execute.c | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 05b4e9f022..6417d21ee1 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -2159,8 +2159,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() diff --git a/src/version.c b/src/version.c index 65b9d0fd72..4c2794af0b 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1516, /**/ 1515, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index a49f3d7c6f..53a748e971 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -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(); From 6c53fca02301ff871cddc1c74c388e23e53a424a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Aug 2020 17:34:46 +0200 Subject: [PATCH 05/19] patch 8.2.1517: cannot easily get the character under the cursor Problem: Cannot easily get the character under the cursor. Solution: Add the {chars} argument to strpart(). --- runtime/doc/eval.txt | 26 +++++++++++++++++--------- src/evalfunc.c | 18 +++++++++++++----- src/testdir/test_functions.vim | 4 ++++ src/version.c | 2 ++ 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 370964e567..09ef979638 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2836,7 +2836,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 @@ -2845,8 +2846,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}]) @@ -3418,7 +3420,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 @@ -9948,17 +9951,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. @@ -9970,8 +9978,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) diff --git a/src/evalfunc.c b/src/evalfunc.c index c8747e2c14..fcfd4b10de 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -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 @@ -8270,10 +8270,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; @@ -8286,6 +8284,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); } diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index dc06bd72ec..e15199b786 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -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() diff --git a/src/version.c b/src/version.c index 4c2794af0b..9eb448b2df 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1517, /**/ 1516, /**/ From 2e80095501238e0c6b702ac7cdfa2e2b763dba28 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Aug 2020 19:34:48 +0200 Subject: [PATCH 06/19] patch 8.2.1518: Vim9: cannot assign to local option Problem: Vim9: cannot assign to local option. Solution: Skip over "&l:" and "&g:". (closes #6749) --- src/ex_docmd.c | 25 ++++++++++++++++++++++--- src/proto/ex_docmd.pro | 1 + src/testdir/test_vim9_script.vim | 25 +++++++++++++++---------- src/testdir/vim9.vim | 5 +++++ src/version.c | 2 ++ src/vim9compile.c | 11 ++++++----- 6 files changed, 51 insertions(+), 18 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 854ff4dfb0..e252b05794 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3242,6 +3242,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. @@ -3273,9 +3294,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)) diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index 1955ccf6cf..3ed152316b 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -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); diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 6417d21ee1..45384d1d8b 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -110,12 +110,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(['¬ex += 3'], 'E113:') CheckDefFailure(['&ts ..= "xxx"'], 'E1019:') @@ -163,19 +172,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' diff --git a/src/testdir/vim9.vim b/src/testdir/vim9.vim index 2f92cf954e..7fbe4a5ed3 100644 --- a/src/testdir/vim9.vim +++ b/src/testdir/vim9.vim @@ -41,6 +41,11 @@ def CheckScriptSuccess(lines: list) delete('Xdef') enddef +def CheckDefAndScriptSuccess(lines: list) + 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, error: string, lnum = -3) diff --git a/src/version.c b/src/version.c index 9eb448b2df..7b52e0546a 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1518, /**/ 1517, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index c0cea2992c..2c3dc70d6d 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4550,8 +4550,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); } @@ -4595,8 +4595,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) { @@ -5131,7 +5131,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 From c2af0afff5c44969ad7611ec2d47d0f52087fa7f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Aug 2020 21:06:02 +0200 Subject: [PATCH 07/19] patch 8.2.1519: Vim9: Ex command default range is not set Problem: Vim9: Ex command default range is not set. Solution: When range is not given use default. (closes #6779) --- src/ex_docmd.c | 222 +++++++++++++++++-------------- src/testdir/test_vim9_script.vim | 12 ++ src/version.c | 2 + 3 files changed, 138 insertions(+), 98 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index e252b05794..3dee01d281 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -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) \ @@ -1880,7 +1882,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; @@ -2282,59 +2286,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) @@ -3011,50 +2963,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++); @@ -3672,6 +3581,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. * @@ -4033,6 +3997,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. */ diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 45384d1d8b..245817b998 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -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' diff --git a/src/version.c b/src/version.c index 7b52e0546a..421e4a68cf 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1519, /**/ 1518, /**/ From b3ea36c5bcb88b6a05a66347eedd461e9385103f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 23 Aug 2020 21:46:32 +0200 Subject: [PATCH 08/19] patch 8.2.1520: Vim9: CTRL-] used in :def function does not work Problem: Vim9: CTRL-] used in :def function does not work. Solution: Omit count or prepend colon. (closes #6769) --- src/normal.c | 4 +++- src/testdir/test_vim9_cmd.vim | 20 ++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/normal.c b/src/normal.c index cb8e736368..1ef6509244 100644 --- a/src/normal.c +++ b/src/normal.c @@ -3644,8 +3644,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); } } diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 9f4231a9a3..9372d90952 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -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! \" + catch /E433/ + caught = 2 + endtry + assert_equal(2, caught) + + try + exe "norm! 3\" + catch /E433/ + caught = 3 + endtry + assert_equal(3, caught) + bwipe! +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 421e4a68cf..41e91880cd 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1520, /**/ 1519, /**/ From 07399e7f078729b03451ba2b342d0cb434ab75cf Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 24 Aug 2020 20:05:50 +0200 Subject: [PATCH 09/19] patch 8.2.1521: reading past end of buffer when reading spellfile Problem: Reading past end of buffer when reading spellfile. (Yegappan Lakshmanan) Solution: Store the byte length and check for it. --- src/spell.h | 1 + src/spellfile.c | 18 +++++++++++------- src/version.c | 2 ++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/spell.h b/src/spell.h index b444145be3..7e4f813b46 100644 --- a/src/spell.h +++ b/src/spell.h @@ -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 diff --git a/src/spellfile.c b/src/spellfile.c index 957be097a6..6aeac86b85 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -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: } // - 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; // - 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; // - 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) * : * 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]; diff --git a/src/version.c b/src/version.c index 41e91880cd..4560103d96 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1521, /**/ 1520, /**/ From c8ec5fe56fc30201c889ca577bf1e69823fadb2a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 24 Aug 2020 20:28:56 +0200 Subject: [PATCH 10/19] patch 8.2.1522: not enough test coverage for the spell file handling Problem: Not enough test coverage for the spell file handling. Solution: Add spell file tests. (Yegappan Lakshmanan, closes #6763) --- src/testdir/test_spellfile.vim | 223 +++++++++++++++++++++++++++++++++ src/version.c | 2 + 2 files changed, 225 insertions(+) diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim index f3578bce2f..6913d40716 100644 --- a/src/testdir/test_spellfile.vim +++ b/src/testdir/test_spellfile.vim @@ -510,6 +510,207 @@ 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() + " No word count in .dic file + call writefile([], 'Xtest.dic') + call writefile([], 'Xtest.aff') + call assert_fails('mkspell! Xtest.spl Xtest', 'E760:') + + " Invalid encoding in .aff file + call writefile(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + call writefile(['SOFOFROM abcdef'], 'Xtest.aff') + let output = execute('mkspell! Xtest.spl Xtest') + call assert_match('Missing SOFOTO line in Xtest.aff', output) + + " FIXME: The following test causes a heap overflow with the ASAN build + " " Both SAL and SOFOFROM/SOFOTO items + " call writefile(['1', 'work'], 'Xtest.dic') + " 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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(['1', 'work'], 'Xtest.dic') + 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) + + 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 +725,26 @@ 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 + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 4560103d96..10f577bdc9 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1522, /**/ 1521, /**/ From b9fc192f928b484c8809fb985ef334d7a2bb5a09 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 25 Aug 2020 21:19:36 +0200 Subject: [PATCH 11/19] patch 8.2.1523: still not enough test coverage for the spell file handling Problem: Still not enough test coverage for the spell file handling. Solution: Add spell file tests. (Yegappan Lakshmanan, closes #6790) --- src/testdir/test_spellfile.vim | 82 +++++++++++++++++++++------------- src/version.c | 2 + 2 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim index 6913d40716..ed11ce0af1 100644 --- a/src/testdir/test_spellfile.vim +++ b/src/testdir/test_spellfile.vim @@ -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 @@ -512,25 +530,29 @@ 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:') - " Invalid encoding in .aff file + " 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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) @@ -558,153 +580,139 @@ func Test_aff_file_format_error() let &encoding = save_encoding " COMPOUNDFORBIDFLAG flag after PFX in an affix file - call writefile(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') call writefile(['SOFOFROM abcdef'], 'Xtest.aff') let output = execute('mkspell! Xtest.spl Xtest') call assert_match('Missing SOFOTO line in Xtest.aff', output) - " FIXME: The following test causes a heap overflow with the ASAN build - " " Both SAL and SOFOFROM/SOFOTO items - " call writefile(['1', 'work'], 'Xtest.dic') - " 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) + " 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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(['1', 'work'], 'Xtest.dic') 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') @@ -747,4 +755,14 @@ func Test_init_spellfile() %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 diff --git a/src/version.c b/src/version.c index 10f577bdc9..f977e28307 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1523, /**/ 1522, /**/ From 2e0866128b6266829a7f38733d5188bc4ec68745 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 25 Aug 2020 22:37:48 +0200 Subject: [PATCH 12/19] patch 8.2.1524: no longer get an error for string concatenation with float Problem: No longer get an error for string concatenation with float. (Tsuyoshi Cho) Solution: Only convert float for Vim9 script. (closes #6787) --- src/eval.c | 11 ++++++----- src/testdir/test_eval_stuff.vim | 6 ++++++ src/version.c | 2 ++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/eval.c b/src/eval.c index 056ac76c2a..2d2ad1b5fa 100644 --- a/src/eval.c +++ b/src/eval.c @@ -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); diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim index 02e7e14fd7..1490ffbc57 100644 --- a/src/testdir/test_eval_stuff.vim +++ b/src/testdir/test_eval_stuff.vim @@ -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 diff --git a/src/version.c b/src/version.c index f977e28307..70fe7df1f7 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1524, /**/ 1523, /**/ From 6e3aeec8461cf27396b6574b5438019a00684e00 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 26 Aug 2020 22:29:57 +0200 Subject: [PATCH 13/19] patch 8.2.1525: messages from tests were not always displayed Problem: Messages from tests were not always displayed. Solution: Always show messages, the timing is always useful. (Ken Takata, closes #6792) --- src/testdir/Make_dos.mak | 2 +- src/testdir/Make_ming.mak | 2 +- src/testdir/Makefile | 4 ++-- src/version.c | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/testdir/Make_dos.mak b/src/testdir/Make_dos.mak index ba728df9eb..89563848ec 100644 --- a/src/testdir/Make_dos.mak +++ b/src/testdir/Make_dos.mak @@ -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) diff --git a/src/testdir/Make_ming.mak b/src/testdir/Make_ming.mak index 00c9e1087a..f9dcfc86e0 100644 --- a/src/testdir/Make_ming.mak +++ b/src/testdir/Make_ming.mak @@ -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) diff --git a/src/testdir/Makefile b/src/testdir/Makefile index 7033d1e46e..6431f6a6b3 100644 --- a/src/testdir/Makefile +++ b/src/testdir/Makefile @@ -12,7 +12,7 @@ SCRIPTSOURCE = ../../runtime # Comment out this line to see the verbose output of tests. # # Catches SwapExists to avoid hanging at the ATTENTION prompt. -REDIR_TEST_TO_NULL = --cmd 'au SwapExists * let v:swapchoice = "e"' > /dev/null +#REDIR_TEST_TO_NULL = --cmd 'au SwapExists * let v:swapchoice = "e"' > /dev/null # Uncomment this line to use valgrind for memory leaks and extra warnings. # The output goes into a file "valgrind.testN" @@ -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) diff --git a/src/version.c b/src/version.c index 70fe7df1f7..71851c8498 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1525, /**/ 1524, /**/ From 228e62975e7aef9d6224a5a7c43625c1c1494fc2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 27 Aug 2020 16:06:46 +0200 Subject: [PATCH 14/19] patch 8.2.1526: line in testdir Makefile got commented out Problem: Line in testdir Makefile got commented out. (Christian Brabandt) Solution: Revert. --- src/testdir/Makefile | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/testdir/Makefile b/src/testdir/Makefile index 6431f6a6b3..2fb329aa1c 100644 --- a/src/testdir/Makefile +++ b/src/testdir/Makefile @@ -12,7 +12,7 @@ SCRIPTSOURCE = ../../runtime # Comment out this line to see the verbose output of tests. # # Catches SwapExists to avoid hanging at the ATTENTION prompt. -#REDIR_TEST_TO_NULL = --cmd 'au SwapExists * let v:swapchoice = "e"' > /dev/null +REDIR_TEST_TO_NULL = --cmd 'au SwapExists * let v:swapchoice = "e"' > /dev/null # Uncomment this line to use valgrind for memory leaks and extra warnings. # The output goes into a file "valgrind.testN" diff --git a/src/version.c b/src/version.c index 71851c8498..514cc6890c 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1526, /**/ 1525, /**/ From 601e76ac3c8fa5d65ac04647a762792ea343ebd5 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 27 Aug 2020 21:33:10 +0200 Subject: [PATCH 15/19] patch 8.2.1527: Vim9: cannot use a function name at script level Problem: Vim9: cannot use a function name as a function reference at script level. Solution: Check if a name is a function name. (closes #6789) --- src/evalvars.c | 14 ++++++++++++++ src/testdir/test_vim9_expr.vim | 11 +++++++++++ src/testdir/test_vim9_script.vim | 19 ++++++++++++------- src/version.c | 2 ++ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/src/evalvars.c b/src/evalvars.c index fbf521e718..a946c75ba7 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -2461,6 +2461,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) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 4ff0fd7e25..e8e7668282 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1668,6 +1668,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, {}) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 245817b998..952b67522d 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -1684,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 @@ -1695,22 +1696,26 @@ def Test_vim9script_funcref_other_script() export def FastFilter(): list return range(10)->filter('FilterFunc') enddef + export def FastFilterDirect(): list + 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 = FastFilter() enddef Test() + def TestDirect() + let x: list = 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() diff --git a/src/version.c b/src/version.c index 514cc6890c..0b63281873 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1527, /**/ 1526, /**/ From 3988f64f9d512fd809d4a600b020638bf2c7d7ec Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 27 Aug 2020 22:43:03 +0200 Subject: [PATCH 16/19] patch 8.2.1528: Vim9: :endif not found after "if false" Problem: Vim9: :endif not found after "if false". Solution: When skipping still check for a following command. (closes #6797) --- src/testdir/test_vim9_script.vim | 8 +++++++ src/version.c | 2 ++ src/vim9compile.c | 39 ++++++++++++++++---------------- 3 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 952b67522d..054fe4cbfc 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -2122,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() diff --git a/src/version.c b/src/version.c index 0b63281873..fed8f1411a 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1528, /**/ 1527, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 2c3dc70d6d..3e9b42c854 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4014,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; @@ -6724,17 +6731,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 @@ -6743,11 +6741,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) @@ -6845,7 +6840,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); @@ -6859,7 +6854,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: @@ -6870,8 +6865,14 @@ 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(). From 749639ec7248740cd271f34ec5f7bb70891b459c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 27 Aug 2020 23:08:47 +0200 Subject: [PATCH 17/19] patch 8.2.1529: Vim9: :elseif may be compiled when not needed Problem: Vim9: :elseif may be compiled when not needed. Solution: Do evaluate the :elseif expression. --- src/testdir/test_vim9_expr.vim | 4 +++- src/version.c | 2 ++ src/vim9compile.c | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index e8e7668282..a7d8d87f3c 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -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) diff --git a/src/version.c b/src/version.c index fed8f1411a..fcc8a46236 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1529, /**/ 1528, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 3e9b42c854..11a75c12c2 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -5519,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) { @@ -5541,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) From 5163fcce792c9d730bf864f4d9bb07f30625cff9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 27 Aug 2020 23:37:09 +0200 Subject: [PATCH 18/19] patch 8.2.1530: Vim9: test fails on MS-Windows Problem: Vim9: test fails on MS-Windows. Solution: Skip Ex command inside "if false". --- src/version.c | 2 ++ src/vim9compile.c | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/version.c b/src/version.c index fcc8a46236..76241f0ec6 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1530, /**/ 1529, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 11a75c12c2..c9805f65a1 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -6879,9 +6879,17 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx) 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: From 7a3330fc578033f06a94c23de61a23edcc59f95e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 27 Aug 2020 23:57:57 +0200 Subject: [PATCH 19/19] patch 8.2.1531: Vim9: test still fails on MS-Windows Problem: Vim9: test still fails on MS-Windows. Solution: When skipping expect function to be NULL. --- src/version.c | 2 ++ src/vim9compile.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/version.c b/src/version.c index 76241f0ec6..35bb3c3972 100644 --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1531, /**/ 1530, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index c9805f65a1..5e4bfdb76c 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -4271,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;