From 6bf821e8abe1da24e5d0624f032d7eda745756e8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 7 Feb 2022 10:33:20 +0000 Subject: [PATCH 01/19] patch 8.2.4315: put in Visual mode not fully tested Problem: Put in Visual mode not fully tested. Solution: Add a few more test cases. (closes #9708) --- src/testdir/test_visual.vim | 46 ++++++++++++++++++++++++++++++++----- src/version.c | 2 ++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim index d336f5b38a..3fbd5c7e4e 100644 --- a/src/testdir/test_visual.vim +++ b/src/testdir/test_visual.vim @@ -1369,18 +1369,52 @@ func Test_visual_paste() call setline(1, ['xxxx']) call setreg('"', 'foo') call setreg('-', 'bar') - normal 1Gvp - call assert_equal(@", 'x') - call assert_equal(@-, 'x') + normal gg0vp + call assert_equal('x', @") + call assert_equal('x', @-) + call assert_equal('fooxxx', getline(1)) + normal $vp + call assert_equal('x', @") + call assert_equal('x', @-) + call assert_equal('fooxxx', getline(1)) + " Test with a different register as unnamed register. + call setline(2, ['baz']) + normal 2gg0"rD + call assert_equal('baz', @") + normal gg0vp + call assert_equal('f', @") + call assert_equal('f', @-) + call assert_equal('bazooxxx', getline(1)) + normal $vp + call assert_equal('x', @") + call assert_equal('x', @-) + call assert_equal('bazooxxf', getline(1)) if has('clipboard') " v_P does not overwrite unnamed register. call setline(1, ['xxxx']) call setreg('"', 'foo') call setreg('-', 'bar') - normal 1GvP - call assert_equal(@", 'foo') - call assert_equal(@-, 'x') + normal gg0vP + call assert_equal('foo', @") + call assert_equal('x', @-) + call assert_equal('fooxxx', getline(1)) + normal $vP + call assert_equal('foo', @") + call assert_equal('x', @-) + call assert_equal('fooxxfoo', getline(1)) + " Test with a different register as unnamed register. + call setline(2, ['baz']) + normal 2gg0"rD + call assert_equal('baz', @") + normal gg0vP + call assert_equal('baz', @") + call assert_equal('f', @-) + call assert_equal('bazooxxfoo', getline(1)) + normal $vP + call assert_equal('baz', @") + call assert_equal('o', @-) + call assert_equal('bazooxxfobaz', getline(1)) endif bwipe! diff --git a/src/version.c b/src/version.c index a071c5f678..8d0dececd1 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4315, /**/ 4314, /**/ From b247e0622ef16b7819f5dadefd3e3f0a803b4021 Mon Sep 17 00:00:00 2001 From: "K.Takata" Date: Mon, 7 Feb 2022 10:45:23 +0000 Subject: [PATCH 02/19] patch 8.2.4316: __CYGWIN32__ is not defined on 64 bit systems Problem: __CYGWIN32__ is not defined on 64 bit systems. Solution: Update #ifdefs. (Ken Takata, closes #9709) --- src/main.c | 2 +- src/os_unix.c | 2 +- src/pty.c | 4 ---- src/version.c | 2 ++ src/vim.h | 4 ++-- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main.c b/src/main.c index 5d02c95849..80cff7cab7 100644 --- a/src/main.c +++ b/src/main.c @@ -2571,7 +2571,7 @@ scripterror: } } #endif -#ifdef __CYGWIN32__ +#ifdef __CYGWIN__ /* * If vim is invoked by non-Cygwin tools, convert away any * DOS paths, so things like .swp files are created correctly. diff --git a/src/os_unix.c b/src/os_unix.c index cbd8ba8bda..a6f3a6c484 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -6341,7 +6341,7 @@ select_eintr: FD_ZERO(&wfds); FD_ZERO(&efds); FD_SET(fd, &rfds); -# if !defined(__QNX__) && !defined(__CYGWIN32__) +# ifndef __QNX__ // For QNX select() always returns 1 if this is set. Why? FD_SET(fd, &efds); # endif diff --git a/src/pty.c b/src/pty.c index 5f8b9e5614..4c4e9e2ead 100644 --- a/src/pty.c +++ b/src/pty.c @@ -46,10 +46,6 @@ #include -#ifdef __CYGWIN32__ -# include -#endif - #ifdef HAVE_SYS_IOCTL_H # include #endif diff --git a/src/version.c b/src/version.c index 8d0dececd1..3caad5f83c 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4316, /**/ 4315, /**/ diff --git a/src/vim.h b/src/vim.h index 86acb6dcda..33d294c9f1 100644 --- a/src/vim.h +++ b/src/vim.h @@ -1576,10 +1576,10 @@ typedef UINT32_TYPEDEF UINT32_T; #endif /* - * EMX doesn't have a global way of making open() use binary I/O. + * Cygwin doesn't have a global way of making open() use binary I/O. * Use O_BINARY for all open() calls. */ -#if defined(__CYGWIN32__) +#ifdef __CYGWIN__ # define O_EXTRA O_BINARY #else # define O_EXTRA 0 From 63ff72aab91679725077eab5c5405267792268bd Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 7 Feb 2022 13:54:01 +0000 Subject: [PATCH 03/19] patch 8.2.4317: MS-Windows: Vim exits when Python 3 initialisation fails Problem: MS-Windows: Vim exits when Python 3 initialisation fails. Solution: Hook into the exit() function to recover from the failure. (Ken Takata, closes #9710) --- src/errors.h | 4 ++ src/if_python3.c | 93 +++++++++++++++++++++++++++++++++++++----- src/os_win32.c | 33 +++++++++++++-- src/proto/os_win32.pro | 1 + src/version.c | 2 + 5 files changed, 119 insertions(+), 14 deletions(-) diff --git a/src/errors.h b/src/errors.h index 80a9a5aaed..53bd0cd26c 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3224,3 +3224,7 @@ EXTERN char e_autoload_import_cannot_use_absolute_or_relative_path[] EXTERN char e_cannot_use_partial_here[] INIT(= N_("E1265: Cannot use a partial here")); #endif +#if defined(FEAT_PYTHON3) && defined(MSWIN) +EXTERN char e_critical_error_in_python3_initialization_check_your_installation[] + INIT(= N_("E1266: Critical error in python3 initialization, check your python3 installation")); +#endif diff --git a/src/if_python3.c b/src/if_python3.c index c8ca3c3792..0b05857d5a 100644 --- a/src/if_python3.c +++ b/src/if_python3.c @@ -112,12 +112,18 @@ typedef PyObject PySliceObject_T; typedef PySliceObject PySliceObject_T; #endif +#ifndef MSWIN +# define HINSTANCE void * +#endif +#if defined(DYNAMIC_PYTHON3) || defined(MSWIN) +static HINSTANCE hinstPy3 = 0; // Instance of python.dll +#endif + #if defined(DYNAMIC_PYTHON3) || defined(PROTO) # ifndef MSWIN # include # define FARPROC void* -# define HINSTANCE void* # if defined(PY_NO_RTLD_GLOBAL) && defined(PY3_NO_RTLD_GLOBAL) # define load_dll(n) dlopen((n), RTLD_LAZY) # else @@ -459,8 +465,6 @@ static void(*py3_PyObject_GC_Del)(void *); static void(*py3_PyObject_GC_UnTrack)(void *); static int (*py3_PyType_IsSubtype)(PyTypeObject *, PyTypeObject *); -static HINSTANCE hinstPy3 = 0; // Instance of python.dll - // Imported exception objects static PyObject *p3imp_PyExc_AttributeError; static PyObject *p3imp_PyExc_IndexError; @@ -1032,13 +1036,8 @@ reset_stdin(void) { FILE *(*py__acrt_iob_func)(unsigned) = NULL; FILE *(*pyfreopen)(const char *, const char *, FILE *) = NULL; - HINSTANCE hinst; + HINSTANCE hinst = hinstPy3; -# ifdef DYNAMIC_PYTHON3 - hinst = hinstPy3; -# else - hinst = GetModuleHandle(PYTHON3_DLL); -# endif if (hinst == NULL || is_stdin_readable()) return; @@ -1061,6 +1060,57 @@ reset_stdin(void) } #else # define reset_stdin() +#endif + +// Python 3.2 or later will abort inside Py_Initialize() when mandatory +// modules cannot be loaded (e.g. 'pythonthreehome' is wrongly set.). +// Install a hook to python dll's exit() and recover from it. +#if defined(MSWIN) && (PY_VERSION_HEX >= 0x030200f0) +# define HOOK_EXIT +# include + +static jmp_buf exit_hook_jump_buf; +static void *orig_exit = NULL; + +/* + * Function that replaces exit() while calling Py_Initialize(). + */ + static void +hooked_exit(int ret) +{ + // Recover from exit. + longjmp(exit_hook_jump_buf, 1); +} + +/* + * Install a hook to python dll's exit(). + */ + static void +hook_py_exit(void) +{ + HINSTANCE hinst = hinstPy3; + + if (hinst == NULL || orig_exit != NULL) + return; + + orig_exit = hook_dll_import_func(hinst, "exit", (void *)hooked_exit); +} + +/* + * Remove the hook installed by hook_py_exit(). + */ + static void +restore_py_exit(void) +{ + HINSTANCE hinst = hinstPy3; + + if (hinst == NULL) + return; + + if (orig_exit != NULL) + hook_dll_import_func(hinst, "exit", orig_exit); + orig_exit = NULL; +} #endif static int @@ -1095,8 +1145,31 @@ Python3_Init(void) PyImport_AppendInittab("vim", Py3Init_vim); +#if !defined(DYNAMIC_PYTHON3) && defined(MSWIN) + hinstPy3 = GetModuleHandle(PYTHON3_DLL); +#endif reset_stdin(); - Py_Initialize(); + +#ifdef HOOK_EXIT + // Catch exit() called in Py_Initialize(). + hook_py_exit(); + if (setjmp(exit_hook_jump_buf) == 0) +#endif + { + Py_Initialize(); +#ifdef HOOK_EXIT + restore_py_exit(); +#endif + } +#ifdef HOOK_EXIT + else + { + // exit() was called in Py_Initialize(). + restore_py_exit(); + emsg(_(e_critical_error_in_python3_initialization_check_your_installation)); + goto fail; + } +#endif #if PY_VERSION_HEX < 0x03090000 // Initialise threads. This is deprecated since Python 3.9. diff --git a/src/os_win32.c b/src/os_win32.c index a01cee5b81..682fdf2bcf 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -572,14 +572,18 @@ mch_is_gui_executable(void) } #endif -#if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) || defined(PROTO) +#if defined(DYNAMIC_ICONV) || defined(DYNAMIC_GETTEXT) \ + || defined(FEAT_PYTHON3) || defined(PROTO) /* * Get related information about 'funcname' which is imported by 'hInst'. * If 'info' is 0, return the function address. * If 'info' is 1, return the module name which the function is imported from. + * If 'info' is 2, hook the function with 'ptr', and return the original + * function address. */ static void * -get_imported_func_info(HINSTANCE hInst, const char *funcname, int info) +get_imported_func_info(HINSTANCE hInst, const char *funcname, int info, + const void *ptr) { PBYTE pImage = (PBYTE)hInst; PIMAGE_DOS_HEADER pDOS = (PIMAGE_DOS_HEADER)hInst; @@ -611,12 +615,23 @@ get_imported_func_info(HINSTANCE hInst, const char *funcname, int info) + (UINT_PTR)(pINT->u1.AddressOfData)); if (strcmp((char *)pImpName->Name, funcname) == 0) { + void *original; + DWORD old, new = PAGE_READWRITE; + switch (info) { case 0: return (void *)pIAT->u1.Function; case 1: return (void *)(pImage + pImpDesc->Name); + case 2: + original = (void *)pIAT->u1.Function; + VirtualProtect(&pIAT->u1.Function, sizeof(void *), + new, &old); + pIAT->u1.Function = (UINT_PTR)ptr; + VirtualProtect(&pIAT->u1.Function, sizeof(void *), + old, &new); + return original; default: return NULL; } @@ -634,7 +649,7 @@ find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname) { char *modulename; - modulename = (char *)get_imported_func_info(hInst, funcname, 1); + modulename = (char *)get_imported_func_info(hInst, funcname, 1, NULL); if (modulename != NULL) return GetModuleHandleA(modulename); return NULL; @@ -646,7 +661,17 @@ find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname) void * get_dll_import_func(HINSTANCE hInst, const char *funcname) { - return get_imported_func_info(hInst, funcname, 0); + return get_imported_func_info(hInst, funcname, 0, NULL); +} + +/* + * Hook the function named 'funcname' which is imported by 'hInst' DLL, + * and return the original function address. + */ + void * +hook_dll_import_func(HINSTANCE hInst, const char *funcname, const void *hook) +{ + return get_imported_func_info(hInst, funcname, 2, hook); } #endif diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro index 5c50816c6f..dac2b7142f 100644 --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -3,6 +3,7 @@ HINSTANCE vimLoadLib(char *name); int mch_is_gui_executable(void); HINSTANCE find_imported_module_by_funcname(HINSTANCE hInst, const char *funcname); void *get_dll_import_func(HINSTANCE hInst, const char *funcname); +void *hook_dll_import_func(HINSTANCE hInst, const char *funcname, const void *hook); int dyn_libintl_init(void); void dyn_libintl_end(void); void PlatformId(void); diff --git a/src/version.c b/src/version.c index 3caad5f83c..956df653a2 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4317, /**/ 4316, /**/ From 54969f4ef5825205ecde09ea80f4087fc3b68e5d Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 7 Feb 2022 13:56:44 +0000 Subject: [PATCH 04/19] patch 8.2.4318: various comment and indent mistakes, returning wrong zero Problem: Various comment and indent mistakes, returning wrong zero. Solution: Fix the mistakes. Return NULL instead of FAIL. --- src/clientserver.c | 2 +- src/eval.c | 10 +++++----- src/evalvars.c | 3 ++- src/version.c | 2 ++ src/vim9cmds.c | 6 +++--- src/window.c | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/clientserver.c b/src/clientserver.c index 793570abd8..caa6fdc668 100644 --- a/src/clientserver.c +++ b/src/clientserver.c @@ -651,7 +651,7 @@ build_drop_cmd( ga_concat(&ga, (char_u *)":"); if (inicmd != NULL) { - // Can't use after "inicmd", because an "startinsert" would cause + // Can't use after "inicmd", because a "startinsert" would cause // the following commands to be inserted as text. Use a "|", // hopefully "inicmd" does allow this... ga_concat(&ga, inicmd); diff --git a/src/eval.c b/src/eval.c index b5ccc4bdfb..7ab05be122 100644 --- a/src/eval.c +++ b/src/eval.c @@ -629,10 +629,10 @@ eval_expr(char_u *arg, exarg_T *eap) */ static char_u * deref_function_name( - char_u **arg, - char_u **tofree, - evalarg_T *evalarg, - int verbose) + char_u **arg, + char_u **tofree, + evalarg_T *evalarg, + int verbose) { typval_T ref; char_u *name = *arg; @@ -2955,7 +2955,7 @@ eval_addlist(typval_T *tv1, typval_T *tv2) /* * Handle fourth level expression: - * + number addition + * + number addition, concatenation of list or blob * - number subtraction * . string concatenation (if script version is 1) * .. string concatenation diff --git a/src/evalvars.c b/src/evalvars.c index b3436067f1..994b9799c8 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -3461,7 +3461,8 @@ set_var_const( semsg(_(e_illegal_variable_name_str), name); goto failed; } - is_script_local = ht == get_script_local_ht() || sid != 0 || var_in_autoload; + is_script_local = ht == get_script_local_ht() || sid != 0 + || var_in_autoload; if (vim9script && !is_script_local diff --git a/src/version.c b/src/version.c index 956df653a2..f30ba25f50 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4318, /**/ 4317, /**/ diff --git a/src/vim9cmds.c b/src/vim9cmds.c index 90b61dbb34..b9a0293925 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -1420,7 +1420,7 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED) { semsg(_(e_separator_mismatch_str), p); vim_free(tofree); - return FAIL; + return NULL; } if (tofree == NULL) len = (int)(end - (p + 1)); @@ -1430,9 +1430,9 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED) vim_free(tofree); p += len + 2 + dropped; if (pat == NULL) - return FAIL; + return NULL; if (generate_PUSHS(cctx, &pat) == FAIL) - return FAIL; + return NULL; if (generate_COMPARE(cctx, EXPR_MATCH, FALSE) == FAIL) return NULL; diff --git a/src/window.c b/src/window.c index f80334d727..4fb92e035d 100644 --- a/src/window.c +++ b/src/window.c @@ -766,7 +766,7 @@ cmd_with_count( } /* - * If "split_disallowed" is set given an error and return FAIL. + * If "split_disallowed" is set give an error and return FAIL. * Otherwise return OK. */ static int From 4e713bafc0ae191b1830e3cd3c323ebd695bc3a1 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 7 Feb 2022 15:31:37 +0000 Subject: [PATCH 05/19] patch 8.2.4319: :put does not work properly in compiled function Problem: :put does not work properly in compiled function. (John Beckett) Solution: Adjust the direction when using line zero. --- src/testdir/test_vim9_cmd.vim | 6 ++++++ src/version.c | 2 ++ src/vim9execute.c | 7 ++++++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 51a62e01f4..0ac69e84fe 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -1156,7 +1156,13 @@ def Test_put_command() :2put =['a', 'b', 'c'] assert_equal(['ppp', 'a', 'b', 'c', 'above'], getline(2, 6)) + :0put ='first' + assert_equal('first', getline(1)) + :1put! ='first again' + assert_equal('first again', getline(1)) + # compute range at runtime + :%del setline(1, range(1, 8)) @a = 'aaa' :$-2put a diff --git a/src/version.c b/src/version.c index f30ba25f50..3535adbeef 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4319, /**/ 4318, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 6c79ff7df3..961e4507c5 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -4617,7 +4617,12 @@ exec_instructions(ectx_T *ectx) // :put! above cursor dir = BACKWARD; else if (lnum >= 0) - curwin->w_cursor.lnum = iptr->isn_arg.put.put_lnum; + { + curwin->w_cursor.lnum = lnum; + if (lnum == 0) + // check_cursor() below will move to line 1 + dir = BACKWARD; + } if (regname == '=') { From 28f1a51bde36e2770dd54c9e2ae69a26cafa5a64 Mon Sep 17 00:00:00 2001 From: qsmodo <75080827+qsmodo@users.noreply.github.com> Date: Mon, 7 Feb 2022 15:57:50 +0000 Subject: [PATCH 06/19] patch 8.2.4320: Athena and Motif: when maximized scrollbar position is wrong Problem: Athena and Motif: when maximized scrollbar position is wrong. Solution: Implement the scrollbar padding functions. (closes #9712) --- src/gui_athena.c | 22 ++++++++++++++++------ src/gui_motif.c | 22 ++++++++++++++++------ src/version.c | 2 ++ 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/gui_athena.c b/src/gui_athena.c index db13bd34f7..6cbabf81d2 100644 --- a/src/gui_athena.c +++ b/src/gui_athena.c @@ -1894,17 +1894,27 @@ gui_mch_set_scrollbar_pos( int gui_mch_get_scrollbar_xpadding(void) { - // TODO: Calculate the padding for adjust scrollbar position when the - // Window is maximized. - return 0; + int xpad; + Dimension tw, ww; + Position tx; + + XtVaGetValues(textArea, XtNwidth, &tw, XtNx, &tx, NULL); + XtVaGetValues(vimShell, XtNwidth, &ww, NULL); + xpad = ww - tw - tx - gui.scrollbar_width; + return (xpad < 0) ? 0 : xpad; } int gui_mch_get_scrollbar_ypadding(void) { - // TODO: Calculate the padding for adjust scrollbar position when the - // Window is maximized. - return 0; + int ypad; + Dimension th, wh; + Position ty; + + XtVaGetValues(textArea, XtNheight, &th, XtNy, &ty, NULL); + XtVaGetValues(vimShell, XtNheight, &wh, NULL); + ypad = wh - th - ty - gui.scrollbar_height; + return (ypad < 0) ? 0 : ypad; } void diff --git a/src/gui_motif.c b/src/gui_motif.c index 8328045bab..ef3747d8d2 100644 --- a/src/gui_motif.c +++ b/src/gui_motif.c @@ -1745,17 +1745,27 @@ gui_mch_set_scrollbar_pos( int gui_mch_get_scrollbar_xpadding(void) { - // TODO: Calculate the padding for adjust scrollbar position when the - // Window is maximized. - return 0; + int xpad; + Dimension tw, ww; + Position tx; + + XtVaGetValues(textArea, XtNwidth, &tw, XtNx, &tx, NULL); + XtVaGetValues(vimShell, XtNwidth, &ww, NULL); + xpad = ww - tw - tx - gui.scrollbar_width; + return (xpad < 0) ? 0 : xpad; } int gui_mch_get_scrollbar_ypadding(void) { - // TODO: Calculate the padding for adjust scrollbar position when the - // Window is maximized. - return 0; + int ypad; + Dimension th, wh; + Position ty; + + XtVaGetValues(textArea, XtNheight, &th, XtNy, &ty, NULL); + XtVaGetValues(vimShell, XtNheight, &wh, NULL); + ypad = wh - th - ty - gui.scrollbar_height; + return (ypad < 0) ? 0 : ypad; } void diff --git a/src/version.c b/src/version.c index 3535adbeef..4fdeb2fdeb 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4320, /**/ 4319, /**/ From 92368aad613bca700877ccb1725e1cb5a80dd34a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 7 Feb 2022 17:50:39 +0000 Subject: [PATCH 07/19] patch 8.2.4321: Vim9: crash when using a funcref to a closure Problem: Vim9: crash when using a funcref to a closure. Solution: Copy pt_outer to the new partial. (closes #9714) --- src/evalfunc.c | 3 +++ src/testdir/test_vim9_func.vim | 22 ++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 27 insertions(+) diff --git a/src/evalfunc.c b/src/evalfunc.c index 3cc95c9884..eb12e75d07 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -4454,6 +4454,9 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref) pt->pt_name = name; func_ref(name); } + + if (arg_pt != NULL) + pt->pt_outer = arg_pt->pt_outer; } rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = pt; diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index e781a21a14..7ab15f72f3 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -3455,6 +3455,28 @@ def Test_nested_lambda_in_closure() delete('XnestedDone') enddef +def Test_nested_closure_funcref() + var lines =<< trim END + vim9script + def Func() + var n: number + def Nested() + ++n + enddef + Nested() + g:result_one = n + var Ref = function(Nested) + Ref() + g:result_two = n + enddef + Func() + END + v9.CheckScriptSuccess(lines) + assert_equal(1, g:result_one) + assert_equal(2, g:result_two) + unlet g:result_one g:result_two +enddef + def Test_check_func_arg_types() var lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c index 4fdeb2fdeb..97bcfdef9b 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4321, /**/ 4320, /**/ From 7aca5ca6763e50d2c23953b20e30fca7457c9abf Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 7 Feb 2022 19:56:43 +0000 Subject: [PATCH 08/19] patch 8.2.4322: Vim9: crash when using funcref with closure Problem: Vim9: crash when using funcref with closure. Solution: Keep a reference to the funcref that has the outer context. (closes #9716) --- src/eval.c | 3 +++ src/evalfunc.c | 5 ++++- src/structs.h | 3 +++ src/testdir/test_vim9_func.vim | 19 +++++++++++++++++++ src/version.c | 2 ++ src/vim9execute.c | 27 +++++++++++++++++++++++---- 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/eval.c b/src/eval.c index 7ab05be122..2942d0fe1d 100644 --- a/src/eval.c +++ b/src/eval.c @@ -4526,6 +4526,9 @@ partial_free(partial_T *pt) // "out_up" is no longer used, decrement refcount on partial that owns it. partial_unref(pt->pt_outer.out_up_partial); + // Using pt_outer from another partial. + partial_unref(pt->pt_outer_partial); + // Decrease the reference count for the context of a closure. If down // to the minimum it may be time to free it. if (pt->pt_funcstack != NULL) diff --git a/src/evalfunc.c b/src/evalfunc.c index eb12e75d07..b031369efd 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -4456,7 +4456,10 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref) } if (arg_pt != NULL) - pt->pt_outer = arg_pt->pt_outer; + { + pt->pt_outer_partial = arg_pt; + ++arg_pt->pt_refcount; + } } rettv->v_type = VAR_PARTIAL; rettv->vval.v_partial = pt; diff --git a/src/structs.h b/src/structs.h index ecab3541d5..1e759f5dfd 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2051,6 +2051,9 @@ struct partial_S // For a compiled closure: the arguments and local variables scope outer_T pt_outer; + // For a partial of a partial: use pt_outer values of this partial. + partial_T *pt_outer_partial; + funcstack_T *pt_funcstack; // copy of stack, used after context // function returns diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 7ab15f72f3..4ac4643e07 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -3477,6 +3477,25 @@ def Test_nested_closure_funcref() unlet g:result_one g:result_two enddef +def Test_nested_closure_in_dict() + var lines =<< trim END + vim9script + def Func(): dict + var n: number + def Inc(): number + ++n + return n + enddef + return {inc: function(Inc)} + enddef + disas Func + var d = Func() + assert_equal(1, d.inc()) + assert_equal(2, d.inc()) + END + v9.CheckScriptSuccess(lines) +enddef + def Test_check_func_arg_types() var lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c index 97bcfdef9b..c1f90cdd03 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4322, /**/ 4321, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 961e4507c5..1412d08365 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -234,6 +234,23 @@ dict_stack_clear(int len) dict_stack_drop(); } +/* + * Get a pointer to useful "pt_outer" of "pt". + */ + static outer_T * +get_pt_outer(partial_T *pt) +{ + partial_T *ptref = pt->pt_outer_partial; + + if (ptref == NULL) + return &pt->pt_outer; + + // partial using partial (recursively) + while (ptref->pt_outer_partial != NULL) + ptref = ptref->pt_outer_partial; + return &ptref->pt_outer; +} + /* * Call compiled function "cdf_idx" from compiled code. * This adds a stack frame and sets the instruction pointer to the start of the @@ -421,13 +438,13 @@ call_dfunc( return FAIL; if (pt != NULL) { - ref->or_outer = &pt->pt_outer; + ref->or_outer = get_pt_outer(pt); ++pt->pt_refcount; ref->or_partial = pt; } else if (ufunc->uf_partial != NULL) { - ref->or_outer = &ufunc->uf_partial->pt_outer; + ref->or_outer = get_pt_outer(ufunc->uf_partial); ++ufunc->uf_partial->pt_refcount; ref->or_partial = ufunc->uf_partial; } @@ -5086,7 +5103,9 @@ call_def_function( goto failed_early; if (partial != NULL) { - if (partial->pt_outer.out_stack == NULL) + outer_T *outer = get_pt_outer(partial); + + if (outer->out_stack == NULL) { if (current_ectx != NULL) { @@ -5099,7 +5118,7 @@ call_def_function( } else { - ectx.ec_outer_ref->or_outer = &partial->pt_outer; + ectx.ec_outer_ref->or_outer = outer; ++partial->pt_refcount; ectx.ec_outer_ref->or_partial = partial; } From f681cfb90b972cb347b3d707c87e48f8accd0e2a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 7 Feb 2022 20:30:57 +0000 Subject: [PATCH 09/19] patch 8.2.4323: Vim9: nested function name can start with "_" Problem: Vim9: nested function name can start with "_". Solution: Use same rule for function name for nested functions. (closes #9713) --- src/testdir/test_vim9_func.vim | 28 ++++++++++++++++++++++++++-- src/version.c | 2 ++ src/vim9compile.c | 5 +++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 4ac4643e07..86b0763dcc 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -679,6 +679,30 @@ def Test_nested_function() assert_equal('ok', g:result) unlet g:result + lines =<< trim END + vim9script + def Outer() + def _Inner() + echo 'bad' + enddef + Inner() + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E128:') + + lines =<< trim END + vim9script + def Outer() + def g:inner() + echo 'bad' + enddef + Inner() + enddef + defcompile + END + v9.CheckScriptFailure(lines, 'E128:') + # nested function inside conditional lines =<< trim END vim9script @@ -3135,11 +3159,11 @@ func Test_partial_call_fails() def Iter(container: any): any var idx = -1 var obj = {state: container} - def g:__NextItem__(self: dict): any + def g:NextItem__(self: dict): any ++idx return self.state[idx] enddef - obj.__next__ = function('g:__NextItem__', [obj]) + obj.__next__ = function('g:NextItem__', [obj]) return obj enddef diff --git a/src/version.c b/src/version.c index c1f90cdd03..2d0bb2d6c0 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4323, /**/ 4322, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index d0479a5fbd..080a53c693 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -886,6 +886,11 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free) } if (check_defined(name_start, name_end - name_start, cctx, FALSE) == FAIL) return NULL; + if (!ASCII_ISUPPER(is_global ? name_start[2] : name_start[0])) + { + semsg(_(e_function_name_must_start_with_capital_or_s_str), name_start); + return NULL; + } eap->arg = name_end; fill_exarg_from_cctx(eap, cctx); From 3787f26c2ed33732a36f26ebe46faeebfe0151af Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 7 Feb 2022 21:54:01 +0000 Subject: [PATCH 10/19] patch 8.2.4324: Vim9: script-local function name can start with "_" Problem: Vim9: script-local function name can start with "_". Solution: Check for leading capital after "s:". Correct error message. --- src/errors.h | 4 ++++ src/testdir/test_vim9_func.vim | 30 +++++++++++++++++++++++------- src/userfunc.c | 24 ++++++++++++++---------- src/version.c | 2 ++ src/vim9compile.c | 2 +- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/errors.h b/src/errors.h index 53bd0cd26c..ecd4eb92d0 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3228,3 +3228,7 @@ EXTERN char e_cannot_use_partial_here[] EXTERN char e_critical_error_in_python3_initialization_check_your_installation[] INIT(= N_("E1266: Critical error in python3 initialization, check your python3 installation")); #endif +#ifdef FEAT_EVAL +EXTERN char e_function_name_must_start_with_capital_str[] + INIT(= N_("E1267: Function name must start with a capital: %s")); +#endif diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 86b0763dcc..ebcd0fbc8e 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -97,7 +97,7 @@ def Test_wrong_function_name() echo 'foo' endfunc END - v9.CheckScriptFailure(lines, 'E128:') + v9.CheckScriptFailure(lines, 'E1267:') lines =<< trim END vim9script @@ -105,7 +105,7 @@ def Test_wrong_function_name() echo 'foo' enddef END - v9.CheckScriptFailure(lines, 'E128:') + v9.CheckScriptFailure(lines, 'E1267:') enddef def Test_autoload_name_mismatch() @@ -685,11 +685,11 @@ def Test_nested_function() def _Inner() echo 'bad' enddef - Inner() + _Inner() enddef defcompile END - v9.CheckScriptFailure(lines, 'E128:') + v9.CheckScriptFailure(lines, 'E1267:') lines =<< trim END vim9script @@ -697,11 +697,27 @@ def Test_nested_function() def g:inner() echo 'bad' enddef - Inner() + g:inner() enddef defcompile END - v9.CheckScriptFailure(lines, 'E128:') + v9.CheckScriptFailure(lines, 'E1267:') + + lines =<< trim END + vim9script + def g:_Func() + echo 'bad' + enddef + END + v9.CheckScriptFailure(lines, 'E1267:') + + lines =<< trim END + vim9script + def s:_Func() + echo 'bad' + enddef + END + v9.CheckScriptFailure(lines, 'E1267:') # nested function inside conditional lines =<< trim END @@ -2772,7 +2788,7 @@ def Test_nested_inline_lambda() lines =<< trim END vim9script - def s:func() + def s:Func() range(10) ->mapnew((_, _) => ({ key: range(10)->mapnew((_, _) => { diff --git a/src/userfunc.c b/src/userfunc.c index 23131b7515..0c54e35749 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3884,15 +3884,6 @@ trans_function_name( // In Vim9 script a user function is script-local by default, unless it // starts with a lower case character: dict.func(). vim9script = ASCII_ISUPPER(*start) && in_vim9script(); - if (vim9script) - { - char_u *p; - - // SomeScript#func() is a global function. - for (p = start; *p != NUL && *p != '('; ++p) - if (*p == AUTOLOAD_CHAR) - vim9script = FALSE; - } /* * Copy the function name to allocated memory. @@ -3904,7 +3895,17 @@ trans_function_name( else if (lead > 0 || vim9script) { if (!vim9script) + { + if (in_vim9script() && lead == 2 && !ASCII_ISUPPER(*lv.ll_name)) + { + semsg(_(in_vim9script() + ? e_function_name_must_start_with_capital_str + : e_function_name_must_start_with_capital_or_s_str), + start); + goto theend; + } lead = 3; + } if (vim9script || (lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) || eval_fname_sid(*pp)) @@ -3925,7 +3926,10 @@ trans_function_name( else if (!(flags & TFN_INT) && (builtin_function(lv.ll_name, len) || (in_vim9script() && *lv.ll_name == '_'))) { - semsg(_(e_function_name_must_start_with_capital_or_s_str), start); + semsg(_(in_vim9script() + ? e_function_name_must_start_with_capital_str + : e_function_name_must_start_with_capital_or_s_str), + start); goto theend; } if (!skip && !(flags & TFN_QUIET) && !(flags & TFN_NO_DEREF)) diff --git a/src/version.c b/src/version.c index 2d0bb2d6c0..cefbd4089f 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4324, /**/ 4323, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 080a53c693..debead4181 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -888,7 +888,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free) return NULL; if (!ASCII_ISUPPER(is_global ? name_start[2] : name_start[0])) { - semsg(_(e_function_name_must_start_with_capital_or_s_str), name_start); + semsg(_(e_function_name_must_start_with_capital_str), name_start); return NULL; } From 3908ef5017a6b4425727013588f72cc7343199b9 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Tue, 8 Feb 2022 12:08:07 +0000 Subject: [PATCH 11/19] patch 8.2.4325: 'wildmenu' only shows few matches Problem: 'wildmenu' only shows few matches. Solution: Add the "pum" option: use a popup menu to show the matches. (Yegappan Lakshmanan et al., closes #9707) --- runtime/doc/options.txt | 31 +++- src/cmdexpand.c | 109 +++++++++++- src/drawscreen.c | 4 + src/evalfunc.c | 2 +- src/ex_getln.c | 55 ++++++- src/option.h | 5 + src/optionstr.c | 2 +- src/popupmenu.c | 10 +- src/proto/cmdexpand.pro | 5 + src/testdir/dumps/Test_wildmenu_pum_01.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_02.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_03.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_04.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_05.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_06.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_07.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_08.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_09.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_10.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_11.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_12.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_13.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_14.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_15.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_16.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_17.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_18.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_19.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_20.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_21.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_22.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_23.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_24.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_25.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_26.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_27.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_28.dump | 10 ++ src/testdir/dumps/Test_wildmenu_pum_29.dump | 10 ++ src/testdir/test_cmdline.vim | 173 ++++++++++++++++++++ src/version.c | 2 + src/vim.h | 2 + 41 files changed, 673 insertions(+), 17 deletions(-) create mode 100644 src/testdir/dumps/Test_wildmenu_pum_01.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_02.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_03.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_04.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_05.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_06.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_07.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_08.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_09.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_10.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_11.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_12.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_13.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_14.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_15.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_16.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_17.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_18.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_19.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_20.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_21.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_22.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_23.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_24.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_25.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_26.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_27.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_28.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_29.dump diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 732e5a74dd..ee2caa7530 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -8974,7 +8974,8 @@ A jump table for the options with a short description can be found at |Q_op|. mode. On pressing 'wildchar' (usually ) to invoke completion, the possible matches are shown just above the command line, with the first match highlighted (overwriting the status line, if there is - one). Keys that show the previous/next match, such as or + one). This is the behavior without "pum" in 'wildoptions. + Keys that show the previous/next match, such as or CTRL-P/CTRL-N, cause the highlight to move to the appropriate match. When 'wildmode' is used, "wildmenu" mode is used where "full" is specified. "longest" and "list" do not start "wildmenu" mode. @@ -8982,10 +8983,12 @@ A jump table for the options with a short description can be found at |Q_op|. If there are more matches than can fit in the line, a ">" is shown on the right and/or a "<" is shown on the left. The status line scrolls as needed. + When 'wildoptions' contains "pum", then the completion matches are + shown in a popup menu. The "wildmenu" mode is abandoned when a key is hit that is not used for selecting a completion. - While the "wildmenu" is active the following keys have special - meanings: + While the "wildmenu" is active, not using the popup menu, the + following keys have special meanings: - select previous/next match (like CTRL-P/CTRL-N) - in filename/menu name completion: move into a @@ -8995,6 +8998,21 @@ A jump table for the options with a short description can be found at |Q_op|. - in filename/menu name completion: move up into parent directory or parent menu. + When using the popup menu for command line completion, the following + keys have special meanings: + - select next match (like CTRL-N) + - in filename/menu name completion: move up into + parent directory or parent menu. + - in filename/menu name completion: move into a + subdirectory or submenu. + - select previous match (like CTRL-P) + CTRL-E - end completion, go back to what was there before + selecting a match. + CTRL-N - go to the next entry + CTRL-P - go to the previous entry + CTRL-Y - accept the currently selected match and stop + completion. + This makes the menus accessible from the console |console-menus|. If you prefer the and keys to move the cursor instead @@ -9057,14 +9075,15 @@ A jump table for the options with a short description can be found at |Q_op|. global {not available when compiled without the |+wildignore| feature} - A list of words that change how command line completion is done. - Currently only one word is allowed: + A list of words that change how |cmdline-completion| is done. + The following values are supported: + pum Display the completion matches using the popupmenu + in the same style as the |ins-completion-menu|. tagfile When using CTRL-D to list matching tags, the kind of tag and the file of the tag is listed. Only one match is displayed per line. Often used tag kinds are: d #define f function - Also see |cmdline-completion|. *'winaltkeys'* *'wak'* 'winaltkeys' 'wak' string (default "menu") diff --git a/src/cmdexpand.c b/src/cmdexpand.c index b75903df98..b37c4f9acb 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -25,6 +25,16 @@ static int expand_shellcmd(char_u *filepat, int *num_file, char_u ***file, int f #if defined(FEAT_EVAL) static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file); static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file); +#endif + +#ifdef FEAT_WILDMENU +// "compl_match_array" points the currently displayed list of entries in the +// popup menu. It is NULL when there is no popup menu. +static pumitem_T *compl_match_array = NULL; +static int compl_match_arraysize; +// First column in cmdline of the matched item for completion. +static int compl_startcol; +static int compl_selected; #endif static int @@ -245,6 +255,42 @@ nextwild( return OK; } +#if defined(FEAT_WILDMENU) || defined(PROTO) +/* + * Display the cmdline completion matches in a popup menu + */ +void cmdline_pum_display(void) +{ + pum_display(compl_match_array, compl_match_arraysize, compl_selected); +} + +int cmdline_pum_active(void) +{ + return p_wmnu && pum_visible() && compl_match_array != NULL; +} + +/* + * Remove the cmdline completion popup menu + */ +void cmdline_pum_remove(void) +{ + pum_undisplay(); + VIM_CLEAR(compl_match_array); + update_screen(0); +} + +void cmdline_pum_cleanup(cmdline_info_T *cclp) +{ + cmdline_pum_remove(); + wildmenu_cleanup(cclp); +} + +int cmdline_compl_startcol(void) +{ + return compl_startcol; +} +#endif + /* * Do wildcard expansion on the string 'str'. * Chars that should not be expanded must be preceded with a backslash. @@ -327,7 +373,12 @@ ExpandOne( findex = -1; } #ifdef FEAT_WILDMENU - if (p_wmnu) + if (compl_match_array) + { + compl_selected = findex; + cmdline_pum_display(); + } + else if (p_wmnu) win_redr_status_matches(xp, xp->xp_numfiles, xp->xp_files, findex, cmd_showtail); #endif @@ -339,6 +390,12 @@ ExpandOne( return NULL; } + if (mode == WILD_CANCEL) + ss = vim_strsave(orig_save ? orig_save : (char_u *)""); + else if (mode == WILD_APPLY) + ss = vim_strsave(findex == -1 ? (orig_save ? + orig_save : (char_u *)"") : xp->xp_files[findex]); + // free old names if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) { @@ -351,7 +408,7 @@ ExpandOne( if (mode == WILD_FREE) // only release file name return NULL; - if (xp->xp_numfiles == -1) + if (xp->xp_numfiles == -1 && mode != WILD_APPLY && mode != WILD_CANCEL) { vim_free(orig_save); orig_save = orig; @@ -553,6 +610,35 @@ showmatches(expand_T *xp, int wildmenu UNUSED) showtail = cmd_showtail; } +#ifdef FEAT_WILDMENU + if (wildmenu && vim_strchr(p_wop, WOP_PUM) != NULL) + { + compl_match_arraysize = num_files; + compl_match_array = ALLOC_MULT(pumitem_T, compl_match_arraysize); + for (i = 0; i < num_files; i++) + { + compl_match_array[i].pum_text = L_SHOWFILE(i); + compl_match_array[i].pum_info = NULL; + compl_match_array[i].pum_extra = NULL; + compl_match_array[i].pum_kind = NULL; + } + compl_startcol = ccline->cmdpos + 1; + columns = vim_strsize(xp->xp_pattern); + if (showtail) + { + columns += vim_strsize(sm_gettail(files_found[0])); + columns -= vim_strsize(files_found[0]); + } + if (columns >= compl_startcol) + compl_startcol = 0; + else + compl_startcol -= columns; + compl_selected = -1; + cmdline_pum_display(); + return EXPAND_OK; + } +#endif + #ifdef FEAT_WILDMENU if (!wildmenu) { @@ -1500,7 +1586,7 @@ set_one_cmd_context( case CMD_tjump: case CMD_stjump: case CMD_ptjump: - if (*p_wop != NUL) + if (vim_strchr(p_wop, WOP_TAGFILE) != NULL) xp->xp_context = EXPAND_TAGS_LISTFILES; else xp->xp_context = EXPAND_TAGS; @@ -2639,6 +2725,22 @@ wildmenu_translate_key( { int c = key; +#ifdef FEAT_WILDMENU + if (p_wmnu && cmdline_pum_active()) + { + // When the popup menu is used, Up/Down keys go to the previous and + // next items in the menu and Left/Right keys go up/down a directory. + if (c == K_UP) + c = K_LEFT; + else if (c == K_DOWN) + c = K_RIGHT; + else if (c == K_LEFT) + c = K_UP; + else if (c == K_RIGHT) + c = K_DOWN; + } +#endif + if (did_wild_list && p_wmnu) { if (c == K_LEFT) @@ -2646,6 +2748,7 @@ wildmenu_translate_key( else if (c == K_RIGHT) c = Ctrl_N; } + // Hitting CR after "emenu Name.": complete submenu if (xp->xp_context == EXPAND_MENUNAMES && p_wmnu && cclp->cmdpos > 1 diff --git a/src/drawscreen.c b/src/drawscreen.c index 5b9619e9fa..6cae313fa0 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -3048,6 +3048,10 @@ redraw_after_callback(int call_update_screen, int do_message) } else if (State & CMDLINE) { +#ifdef FEAT_WILDMENU + if (pum_visible()) + cmdline_pum_display(); +#endif // Don't redraw when in prompt_for_number(). if (cmdline_row > 0) { diff --git a/src/evalfunc.c b/src/evalfunc.c index b031369efd..db0d1ceedb 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -10336,7 +10336,7 @@ f_visualmode(typval_T *argvars, typval_T *rettv) f_wildmenumode(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { #ifdef FEAT_WILDMENU - if (wild_menu_showing) + if (wild_menu_showing || ((State & CMDLINE) && cmdline_pum_active())) rettv->vval.v_number = 1; #endif } diff --git a/src/ex_getln.c b/src/ex_getln.c index 097b97eeb3..5def8a6a24 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -924,9 +924,18 @@ cmdline_wildchar_complete( // if 'wildmode' contains "list" may still need to list if (xp->xp_numfiles > 1 && !*did_wild_list - && (wim_flags[wim_index] & WIM_LIST)) + && ((wim_flags[wim_index] & WIM_LIST) +#ifdef FEAT_WILDMENU + || (p_wmnu && (wim_flags[wim_index] & WIM_FULL) != 0) +#endif + )) { +#ifdef FEAT_WILDMENU + (void)showmatches(xp, + p_wmnu && ((wim_flags[wim_index] & WIM_LIST) == 0)); +#else (void)showmatches(xp, FALSE); +#endif redrawcmd(); *did_wild_list = TRUE; } @@ -1848,6 +1857,23 @@ getcmdline_int( #ifdef FEAT_WILDMENU c = wildmenu_translate_key(&ccline, c, &xpc, did_wild_list); + + if (cmdline_pum_active()) + { + if (c == Ctrl_E || c == Ctrl_Y) + { + int wild_type; + + wild_type = (c == Ctrl_E) ? WILD_CANCEL : WILD_APPLY; + + if (nextwild(&xpc, wild_type, WILD_NO_BEEP, + firstc != '@') == FAIL) + break; + cmdline_pum_cleanup(&ccline); + xpc.xp_context = EXPAND_NOTHING; + goto cmdline_changed; + } + } #endif // free expanded names when finished walking through matches @@ -1856,6 +1882,9 @@ getcmdline_int( && c != Ctrl_N && c != Ctrl_P && c != Ctrl_A && c != Ctrl_L) { +#ifdef FEAT_WILDMENU + cmdline_pum_remove(); +#endif (void)ExpandOne(&xpc, NULL, NULL, 0, WILD_FREE); did_wild_list = FALSE; #ifdef FEAT_WILDMENU @@ -1950,10 +1979,19 @@ getcmdline_int( // goes to last match, in a clumsy way if (c == K_S_TAB && KeyTyped) { - if (nextwild(&xpc, WILD_EXPAND_KEEP, 0, firstc != '@') == OK - && nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK - && nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK) - goto cmdline_changed; + if (nextwild(&xpc, WILD_EXPAND_KEEP, 0, firstc != '@') == OK) + { +#ifdef FEAT_WILDMENU + // Trigger the popup menu when wildoptions=pum + showmatches(&xpc, + p_wmnu && ((wim_flags[wim_index] & WIM_LIST) == 0)); +#else + (void)showmatches(&xpc, FALSE); +#endif + if (nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK + && nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK) + goto cmdline_changed; + } } if (c == NUL || c == K_ZERO) // NUL is stored as NL @@ -2222,6 +2260,13 @@ getcmdline_int( case Ctrl_A: // all matches if (nextwild(&xpc, WILD_ALL, 0, firstc != '@') == FAIL) break; +#ifdef FEAT_WILDMENU + if (cmdline_pum_active()) + { + cmdline_pum_cleanup(&ccline); + xpc.xp_context = EXPAND_NOTHING; + } +#endif goto cmdline_changed; case Ctrl_L: diff --git a/src/option.h b/src/option.h index c79eb0bdd7..ebbf94b069 100644 --- a/src/option.h +++ b/src/option.h @@ -356,6 +356,11 @@ typedef enum { #define WIM_LIST 0x04 #define WIM_BUFLASTUSED 0x08 +// flags for the 'wildoptions' option +// each defined char should be unique over all values. +#define WOP_TAGFILE 't' +#define WOP_PUM 'p' + // arguments for can_bs() // each defined char should be unique over all values // except for BS_START, that intentionally also matches BS_NOSTOP diff --git a/src/optionstr.c b/src/optionstr.c index 8d74ba0d59..c8bef03099 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -57,7 +57,7 @@ static char *(p_tbis_values[]) = {"tiny", "small", "medium", "large", "huge", "g static char *(p_ttym_values[]) = {"xterm", "xterm2", "dec", "netterm", "jsbterm", "pterm", "urxvt", "sgr", NULL}; #endif static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", "none", "NONE", NULL}; -static char *(p_wop_values[]) = {"tagfile", NULL}; +static char *(p_wop_values[]) = {"tagfile", "pum", NULL}; #ifdef FEAT_WAK static char *(p_wak_values[]) = {"yes", "menu", "no", NULL}; #endif diff --git a/src/popupmenu.c b/src/popupmenu.c index cf5558b074..cf2f2eff97 100644 --- a/src/popupmenu.c +++ b/src/popupmenu.c @@ -116,7 +116,10 @@ pum_display( // Remember the essential parts of the window position and size, so we // can decide when to reposition the popup menu. pum_window = curwin; - pum_win_row = curwin->w_wrow + W_WINROW(curwin); + if (State == CMDLINE) + pum_win_row = cmdline_row; + else + pum_win_row = curwin->w_wrow + W_WINROW(curwin); pum_win_height = curwin->w_height; pum_win_col = curwin->w_wincol; pum_win_wcol = curwin->w_wcol; @@ -215,6 +218,11 @@ pum_display( max_width = pum_base_width; // Calculate column +#ifdef FEAT_WILDMENU + if (State == CMDLINE) + cursor_col = cmdline_compl_startcol(); + else +#endif #ifdef FEAT_RIGHTLEFT if (curwin->w_p_rl) cursor_col = curwin->w_wincol + curwin->w_width diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro index 1c4f954394..b64c6bbdc6 100644 --- a/src/proto/cmdexpand.pro +++ b/src/proto/cmdexpand.pro @@ -3,6 +3,11 @@ int nextwild(expand_T *xp, int type, int options, int escape); char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode); void ExpandInit(expand_T *xp); void ExpandCleanup(expand_T *xp); +void cmdline_pum_display(void); +int cmdline_pum_active(void); +void cmdline_pum_remove(void); +void cmdline_pum_cleanup(cmdline_info_T *cclp); +int cmdline_compl_startcol(void); int showmatches(expand_T *xp, int wildmenu); char_u *sm_gettail(char_u *s); char_u *addstar(char_u *fname, int len, int context); diff --git a/src/testdir/dumps/Test_wildmenu_pum_01.dump b/src/testdir/dumps/Test_wildmenu_pum_01.dump new file mode 100644 index 0000000000..8c48229949 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_01.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @3| +0#0000001#e0e0e08|d|e|f|i|n|e| @8| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|j|u|m|p| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|l|i|s|t| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|p|l|a|c|e| @9| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@53 +|:+0#0000000&|s|i|g|n| |d|e|f|i|n|e> @62 diff --git a/src/testdir/dumps/Test_wildmenu_pum_02.dump b/src/testdir/dumps/Test_wildmenu_pum_02.dump new file mode 100644 index 0000000000..f118df78e1 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_02.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @3| +0#0000001#ffd7ff255|d|e|f|i|n|e| @8| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|j|u|m|p| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#e0e0e08|l|i|s|t| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|p|l|a|c|e| @9| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@53 +|:+0#0000000&|s|i|g|n| |l|i|s|t> @64 diff --git a/src/testdir/dumps/Test_wildmenu_pum_03.dump b/src/testdir/dumps/Test_wildmenu_pum_03.dump new file mode 100644 index 0000000000..c3276ab387 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_03.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @3| +0#0000001#ffd7ff255|d|e|f|i|n|e| @8| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|j|u|m|p| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|l|i|s|t| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#e0e0e08|p|l|a|c|e| @9| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@53 +|:+0#0000000&|s|i|g|n| |p|l|a|c|e> @63 diff --git a/src/testdir/dumps/Test_wildmenu_pum_04.dump b/src/testdir/dumps/Test_wildmenu_pum_04.dump new file mode 100644 index 0000000000..f118df78e1 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_04.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @3| +0#0000001#ffd7ff255|d|e|f|i|n|e| @8| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|j|u|m|p| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#e0e0e08|l|i|s|t| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|p|l|a|c|e| @9| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@53 +|:+0#0000000&|s|i|g|n| |l|i|s|t> @64 diff --git a/src/testdir/dumps/Test_wildmenu_pum_05.dump b/src/testdir/dumps/Test_wildmenu_pum_05.dump new file mode 100644 index 0000000000..1c3a2e885a --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_05.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @3| +0#0000001#ffd7ff255|d|e|f|i|n|e| @8| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#e0e0e08|j|u|m|p| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|l|i|s|t| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|p|l|a|c|e| @9| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@53 +|:+0#0000000&|s|i|g|n| |j|u|m|p> @64 diff --git a/src/testdir/dumps/Test_wildmenu_pum_06.dump b/src/testdir/dumps/Test_wildmenu_pum_06.dump new file mode 100644 index 0000000000..4b60c5745f --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_06.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|s|i|g|n| > @68 diff --git a/src/testdir/dumps/Test_wildmenu_pum_07.dump b/src/testdir/dumps/Test_wildmenu_pum_07.dump new file mode 100644 index 0000000000..20a39ab3ff --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_07.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|s|i|g|n| |u|n|p|l|a|c|e> @61 diff --git a/src/testdir/dumps/Test_wildmenu_pum_08.dump b/src/testdir/dumps/Test_wildmenu_pum_08.dump new file mode 100644 index 0000000000..73547387eb --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_08.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|s|i|g|n| |u|n> @66 diff --git a/src/testdir/dumps/Test_wildmenu_pum_09.dump b/src/testdir/dumps/Test_wildmenu_pum_09.dump new file mode 100644 index 0000000000..2e8a0a16b3 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_09.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @3| +0#0000001#e0e0e08|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@53 +|:+0#0000000&|s|i|g|n| |u|n|d|e|f|i|n|e> @60 diff --git a/src/testdir/dumps/Test_wildmenu_pum_10.dump b/src/testdir/dumps/Test_wildmenu_pum_10.dump new file mode 100644 index 0000000000..9851b7ce3f --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_10.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @3| +0#0000001#ffd7ff255|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#e0e0e08|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@53 +|:+0#0000000&|s|i|g|n| |u|n|p|l|a|c|e> @61 diff --git a/src/testdir/dumps/Test_wildmenu_pum_11.dump b/src/testdir/dumps/Test_wildmenu_pum_11.dump new file mode 100644 index 0000000000..4697c8a562 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_11.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @10| +0#0000001#e0e0e08|c|u|l|h|l|=| @8| +0#4040ff13#ffffff0@46 +|~| @10| +0#0000001#ffd7ff255|i|c|o|n|=| @9| +0#4040ff13#ffffff0@46 +|~| @10| +0#0000001#ffd7ff255|l|i|n|e|h|l|=| @7| +0#4040ff13#ffffff0@46 +|~| @10| +0#0000001#ffd7ff255|n|u|m|h|l|=| @8| +0#4040ff13#ffffff0@46 +|~| @10| +0#0000001#ffd7ff255|t|e|x|t|=| @9| +0#4040ff13#ffffff0@46 +|~| @10| +0#0000001#ffd7ff255|t|e|x|t|h|l|=| @7| +0#4040ff13#ffffff0@46 +|:+0#0000000&|s|i|g|n| |d|e|f|i|n|e| |c|u|l|h|l|=> @55 diff --git a/src/testdir/dumps/Test_wildmenu_pum_12.dump b/src/testdir/dumps/Test_wildmenu_pum_12.dump new file mode 100644 index 0000000000..d93631de77 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_12.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @17| +0#0000001#e0e0e08|c|u|l|h|l|=| @8| +0#4040ff13#ffffff0@39 +|~| @17| +0#0000001#ffd7ff255|i|c|o|n|=| @9| +0#4040ff13#ffffff0@39 +|~| @17| +0#0000001#ffd7ff255|l|i|n|e|h|l|=| @7| +0#4040ff13#ffffff0@39 +|~| @17| +0#0000001#ffd7ff255|n|u|m|h|l|=| @8| +0#4040ff13#ffffff0@39 +|~| @17| +0#0000001#ffd7ff255|t|e|x|t|=| @9| +0#4040ff13#ffffff0@39 +|~| @17| +0#0000001#ffd7ff255|t|e|x|t|h|l|=| @7| +0#4040ff13#ffffff0@39 +|:+0#0000000&|s|i|g|n| |d|e|f|i|n|e| |c|u|l|h|l|=| |c|u|l|h|l|=> @48 diff --git a/src/testdir/dumps/Test_wildmenu_pum_13.dump b/src/testdir/dumps/Test_wildmenu_pum_13.dump new file mode 100644 index 0000000000..b2b1424f56 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_13.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @24| +0#0000001#e0e0e08|c|u|l|h|l|=| @8| +0#4040ff13#ffffff0@32 +|~| @24| +0#0000001#ffd7ff255|i|c|o|n|=| @9| +0#4040ff13#ffffff0@32 +|~| @24| +0#0000001#ffd7ff255|l|i|n|e|h|l|=| @7| +0#4040ff13#ffffff0@32 +|~| @24| +0#0000001#ffd7ff255|n|u|m|h|l|=| @8| +0#4040ff13#ffffff0@32 +|~| @24| +0#0000001#ffd7ff255|t|e|x|t|=| @9| +0#4040ff13#ffffff0@32 +|~| @24| +0#0000001#ffd7ff255|t|e|x|t|h|l|=| @7| +0#4040ff13#ffffff0@32 +|:+0#0000000&|s|i|g|n| |d|e|f|i|n|e| |c|u|l|h|l|=| |c|u|l|h|l|=| |c|u|l|h|l|=> @41 diff --git a/src/testdir/dumps/Test_wildmenu_pum_14.dump b/src/testdir/dumps/Test_wildmenu_pum_14.dump new file mode 100644 index 0000000000..78da67f409 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_14.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @5| +0#0000001#e0e0e08|X|d|i|r|A|/| @8| +0#4040ff13#ffffff0@51 +|~| @5| +0#0000001#ffd7ff255|X|f|i|l|e|A| @8| +0#4040ff13#ffffff0@51 +|:+0#0000000&|e| |X|d|i|r|/|X|d|i|r|A|/> @60 diff --git a/src/testdir/dumps/Test_wildmenu_pum_15.dump b/src/testdir/dumps/Test_wildmenu_pum_15.dump new file mode 100644 index 0000000000..fa4f50b2f6 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_15.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @11| +0#0000001#e0e0e08|X|d|i|r|B|/| @8| +0#4040ff13#ffffff0@45 +|~| @11| +0#0000001#ffd7ff255|X|f|i|l|e|B| @8| +0#4040ff13#ffffff0@45 +|:+0#0000000&|e| |X|d|i|r|/|X|d|i|r|A|/|X|d|i|r|B|/> @54 diff --git a/src/testdir/dumps/Test_wildmenu_pum_16.dump b/src/testdir/dumps/Test_wildmenu_pum_16.dump new file mode 100644 index 0000000000..78da67f409 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_16.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @5| +0#0000001#e0e0e08|X|d|i|r|A|/| @8| +0#4040ff13#ffffff0@51 +|~| @5| +0#0000001#ffd7ff255|X|f|i|l|e|A| @8| +0#4040ff13#ffffff0@51 +|:+0#0000000&|e| |X|d|i|r|/|X|d|i|r|A|/> @60 diff --git a/src/testdir/dumps/Test_wildmenu_pum_17.dump b/src/testdir/dumps/Test_wildmenu_pum_17.dump new file mode 100644 index 0000000000..5eef94d82a --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_17.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|s|i|g|n| |d|e|f|i|n|e| |j|u|m|p| |l|i|s|t| |p|l|a|c|e| |u|n|d|e|f|i|n|e| |u|n|p|l|a|c|e> @29 diff --git a/src/testdir/dumps/Test_wildmenu_pum_18.dump b/src/testdir/dumps/Test_wildmenu_pum_18.dump new file mode 100644 index 0000000000..09b0b3e609 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_18.dump @@ -0,0 +1,10 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|s|i|g|n| |d|e|f|i|n|e| @62 +|d|e|f|i|n|e| @68 +|:|s|i|g|n| |d|e|f|i|n|e> @62 diff --git a/src/testdir/dumps/Test_wildmenu_pum_19.dump b/src/testdir/dumps/Test_wildmenu_pum_19.dump new file mode 100644 index 0000000000..fa51c76eb6 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_19.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @3| +0#0000001#ffd7ff255|d|e|f|i|n|e| @8| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|j|u|m|p| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|l|i|s|t| @10| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|p|l|a|c|e| @9| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#e0e0e08|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@53 +|:+0#0000000&|s|i|g|n| |u|n|d|e|f|i|n|e> @60 diff --git a/src/testdir/dumps/Test_wildmenu_pum_20.dump b/src/testdir/dumps/Test_wildmenu_pum_20.dump new file mode 100644 index 0000000000..99c2900a80 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_20.dump @@ -0,0 +1,10 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@74 diff --git a/src/testdir/dumps/Test_wildmenu_pum_21.dump b/src/testdir/dumps/Test_wildmenu_pum_21.dump new file mode 100644 index 0000000000..ca379318eb --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_21.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|s|i|g|n| |d|e|f|i|n|e|x> @61 diff --git a/src/testdir/dumps/Test_wildmenu_pum_22.dump b/src/testdir/dumps/Test_wildmenu_pum_22.dump new file mode 100644 index 0000000000..3fd00ade37 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_22.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|[+1&&|N|o| |N|a|m|e|]| @65 +|:+0#4040ff13&|s+0#af5f00255&|e|t| +0#0000000&|w+0#e000e06&|i|l|d|m|o|d|e|=+0#0000000&|l|o|n|g|e|s|t|,+0#af5f00255&|f+0#0000000&|u|l@1| @48 +|:+0#4040ff13&|s+0#af5f00255&|e|t| +0#0000000&|w+0#e000e06&|i|l|d|m|o|d|e|=+0#0000000&|f|u|l@1| @56 +|:+0#4040ff13&|s+0#af5f00255&|i|g|n| +0#0000000&|d|e|f|i|n|e| @62 +|:+0#4040ff13&|s+0#af5f00255&|i|g|n| +0#0000000&|d|e|f|i|n|e> @62 +|~+0#4040ff13&| @73 +|~| @73 +|[+3#0000000&|C|o|m@1|a|n|d| |L|i|n|e|]| @60 +|Y+0#0000001#ffff4012|o|u| |d|i|s|c|o|v|e|r|e|d| |t|h|e| |c|o|m@1|a|n|d|-|l|i|n|e| |w|i|n|d|o|w|!| |Y|o|u| |c|a|n| |c|l|o|s|e| |i|t| |w|i|t|h| |"|:|q|"|.| +0#0000000#ffffff0@7 diff --git a/src/testdir/dumps/Test_wildmenu_pum_23.dump b/src/testdir/dumps/Test_wildmenu_pum_23.dump new file mode 100644 index 0000000000..0bd624350c --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_23.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @3| +0#0000001#ffd7ff255|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@53 +|:+0#0000000&|s|i|g|n| |u> @67 diff --git a/src/testdir/dumps/Test_wildmenu_pum_24.dump b/src/testdir/dumps/Test_wildmenu_pum_24.dump new file mode 100644 index 0000000000..f5767f7f04 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_24.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000001#e0e0e08|b|u|f|d|o| @9| +0#4040ff13#ffffff0@58 +| +0#0000001#ffd7ff255|b|u|f@1|e|r| @8| +0#4040ff13#ffffff0@58 +| +0#0000001#ffd7ff255|b|u|f@1|e|r|s| @7| +0#4040ff13#ffffff0@58 +| +0#0000001#ffd7ff255|b|u|n|l|o|a|d| @7| +0#4040ff13#ffffff0@58 +|:+0#0000000&|b|u|f|d|o> @68 diff --git a/src/testdir/dumps/Test_wildmenu_pum_25.dump b/src/testdir/dumps/Test_wildmenu_pum_25.dump new file mode 100644 index 0000000000..b7d117d829 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_25.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|b|u|f|d>o| @68 diff --git a/src/testdir/dumps/Test_wildmenu_pum_26.dump b/src/testdir/dumps/Test_wildmenu_pum_26.dump new file mode 100644 index 0000000000..30786c9d01 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_26.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|s|i|g|n| |d|e|f|i|n> @63 diff --git a/src/testdir/dumps/Test_wildmenu_pum_27.dump b/src/testdir/dumps/Test_wildmenu_pum_27.dump new file mode 100644 index 0000000000..4b60c5745f --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_27.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|s|i|g|n| > @68 diff --git a/src/testdir/dumps/Test_wildmenu_pum_28.dump b/src/testdir/dumps/Test_wildmenu_pum_28.dump new file mode 100644 index 0000000000..910e224a08 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_28.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&> @73 diff --git a/src/testdir/dumps/Test_wildmenu_pum_29.dump b/src/testdir/dumps/Test_wildmenu_pum_29.dump new file mode 100644 index 0000000000..7a82fce735 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_29.dump @@ -0,0 +1,10 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|s|i|g|n| |x|y|z> @65 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index abd12467dd..7faf811dbd 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -1965,4 +1965,177 @@ func Test_suffixes_opt() call delete('Xfile.o') endfunc +" Test for using a popup menu for the command line completion matches +" (wildoptions=pum) +func Test_wildmenu_pum() + CheckRunVimInTerminal + + let commands =<< trim [CODE] + set wildmenu + set wildoptions=pum + set shm+=I + set noruler + set noshowcmd + [CODE] + call writefile(commands, 'Xtest') + + let buf = RunVimInTerminal('-S Xtest', #{rows: 10}) + + call term_sendkeys(buf, ":sign \") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_01', {}) + + call term_sendkeys(buf, "\\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_02', {}) + + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_03', {}) + + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_04', {}) + + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_05', {}) + + " pressing should end completion and go back to the original match + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_06', {}) + + " pressing should select the current match and end completion + call term_sendkeys(buf, "\\\\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_07', {}) + + " With 'wildmode' set to 'longest,full', completing a match should display + " the longest match, the wildmenu should not be displayed. + call term_sendkeys(buf, ":\set wildmode=longest,full\") + call TermWait(buf) + call term_sendkeys(buf, ":sign u\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_08', {}) + + " pressing should display the wildmenu + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_09', {}) + + " pressing second time should select the next entry in the menu + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_10', {}) + + call term_sendkeys(buf, ":\set wildmode=full\") + " " showing popup menu in different columns in the cmdline + call term_sendkeys(buf, ":sign define \") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_11', {}) + + call term_sendkeys(buf, " \") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_12', {}) + + call term_sendkeys(buf, " \") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_13', {}) + + " Directory name completion + call mkdir('Xdir/XdirA/XdirB', 'p') + call writefile([], 'Xdir/XfileA') + call writefile([], 'Xdir/XdirA/XfileB') + call writefile([], 'Xdir/XdirA/XdirB/XfileC') + + call term_sendkeys(buf, "\e Xdi\\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_14', {}) + + " Pressing on a directory name should go into that directory + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_15', {}) + + " Pressing on a directory name should go to the parent directory + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_16', {}) + + " Pressing when the popup menu is displayed should list all the + " matches and remove the popup menu + call term_sendkeys(buf, "\sign \\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_17', {}) + + " Pressing when the popup menu is displayed should remove the popup + " menu + call term_sendkeys(buf, "\sign \\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_18', {}) + + " Pressing should open the popup menu with the last entry selected + call term_sendkeys(buf, "\\:sign \\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_19', {}) + + " Pressing should close the popup menu and cancel the cmd line + call term_sendkeys(buf, "\\:sign \\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_20', {}) + + " Typing a character when the popup is open, should close the popup + call term_sendkeys(buf, ":sign \x") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_21', {}) + + " When the popup is open, entering the cmdline window should close the popup + call term_sendkeys(buf, "\sign \\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_22', {}) + call term_sendkeys(buf, ":q\") + + " After the last popup menu item, should show the original string + call term_sendkeys(buf, ":sign u\\\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_23', {}) + + " Use the popup menu for the command name + call term_sendkeys(buf, "\bu\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_24', {}) + + " Pressing the left arrow should remove the popup menu + call term_sendkeys(buf, "\\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_25', {}) + + " Pressing should remove the popup menu and erase the last character + call term_sendkeys(buf, "\\sign \\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_26', {}) + + " Pressing should remove the popup menu and erase the previous word + call term_sendkeys(buf, "\\sign \\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_27', {}) + + " Pressing should remove the popup menu and erase the entire line + call term_sendkeys(buf, "\\sign \\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_28', {}) + + " Using to cancel the popup menu and then pressing should recall + " the cmdline from history + call term_sendkeys(buf, "sign xyz\:sign \\\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_29', {}) + + call term_sendkeys(buf, "\\") + call StopVimInTerminal(buf) + call delete('Xtest') + call delete('Xdir', 'rf') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index cefbd4089f..ff7f2f0291 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4325, /**/ 4324, /**/ diff --git a/src/vim.h b/src/vim.h index 33d294c9f1..67b4e33c1b 100644 --- a/src/vim.h +++ b/src/vim.h @@ -809,6 +809,8 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring); #define WILD_ALL 6 #define WILD_LONGEST 7 #define WILD_ALL_KEEP 8 +#define WILD_CANCEL 9 +#define WILD_APPLY 10 #define WILD_LIST_NOTFOUND 0x01 #define WILD_HOME_REPLACE 0x02 From 51ab7c7d0da08aac796acff22a6c075dac579e76 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 8 Feb 2022 12:58:37 +0000 Subject: [PATCH 12/19] patch 8.2.4326: "o" and "O" copying comment not sufficiently tested Problem: "o" and "O" copying comment not sufficiently tested. Solution: Add a test case. (closes #9718) --- src/testdir/test_textformat.vim | 28 +++++++++++++++++++++++++++- src/version.c | 2 ++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_textformat.vim b/src/testdir/test_textformat.vim index 6402989297..385021396d 100644 --- a/src/testdir/test_textformat.vim +++ b/src/testdir/test_textformat.vim @@ -238,7 +238,33 @@ func Test_format_c_comment() END call assert_equal(expected, getline(1, '$')) - " Using "o" repeats the line comment, "O" does not. + " Using either "o" or "O" repeats a line comment occupying a whole line. + %del + let text =<< trim END + nop; + // This is a comment + val = val; + END + call setline(1, text) + normal 2Go + let expected =<< trim END + nop; + // This is a comment + // + val = val; + END + call assert_equal(expected, getline(1, '$')) + normal 2GO + let expected =<< trim END + nop; + // + // This is a comment + // + val = val; + END + call assert_equal(expected, getline(1, '$')) + + " Using "o" repeats a line comment after a statement, "O" does not. %del let text =<< trim END nop; diff --git a/src/version.c b/src/version.c index ff7f2f0291..82d12a04b4 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4326, /**/ 4325, /**/ From e3537aec2f8d6470010547af28dcbd83d41461b8 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 8 Feb 2022 15:05:20 +0000 Subject: [PATCH 13/19] patch 8.2.4327: may end up with no current buffer Problem: May end up with no current buffer. Solution: When deleting the current buffer to not pick a quickfix buffer as the new current buffer. --- src/buffer.c | 26 ++++++++++++++++++++++---- src/testdir/test_quickfix.vim | 25 +++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 81bdb31ca1..b3e2bc3f98 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1430,8 +1430,14 @@ do_buffer_ext( buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum); if (buf != NULL) { - if (buf == curbuf || !buf->b_p_bl) - buf = NULL; // skip current and unlisted bufs + // Skip current and unlisted bufs. Also skip a quickfix + // buffer, it might be deleted soon. + if (buf == curbuf || !buf->b_p_bl +#if defined(FEAT_QUICKFIX) + || bt_quickfix(buf) +#endif + ) + buf = NULL; else if (buf->b_ml.ml_mfp == NULL) { // skip unloaded buf, but may keep it for later @@ -1467,7 +1473,11 @@ do_buffer_ext( continue; } // in non-help buffer, try to skip help buffers, and vv - if (buf->b_help == curbuf->b_help && buf->b_p_bl) + if (buf->b_help == curbuf->b_help && buf->b_p_bl +#if defined(FEAT_QUICKFIX) + && !bt_quickfix(buf) +#endif + ) { if (buf->b_ml.ml_mfp != NULL) // found loaded buffer break; @@ -1485,7 +1495,11 @@ do_buffer_ext( if (buf == NULL) // No loaded buffer, find listed one { FOR_ALL_BUFFERS(buf) - if (buf->b_p_bl && buf != curbuf) + if (buf->b_p_bl && buf != curbuf +#if defined(FEAT_QUICKFIX) + && !bt_quickfix(buf) +#endif + ) break; } if (buf == NULL) // Still no buffer, just take one @@ -1494,6 +1508,10 @@ do_buffer_ext( buf = curbuf->b_next; else buf = curbuf->b_prev; +#if defined(FEAT_QUICKFIX) + if (bt_quickfix(buf)) + buf = NULL; +#endif } } diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 07fdb9644b..adb0ea4fd5 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -5851,5 +5851,30 @@ func Test_lopen_bwipe() delfunc R endfunc +" Another sequence of commands that caused all buffers to be wiped out +func Test_lopen_bwipe_all() + let lines =<< trim END + func R() + silent! tab lopen + e foo + silent! lfile + endfunc + cal R() + exe "norm \\0" + cal R() + bwipe + + call writefile(['done'], 'Xresult') + qall! + END + call writefile(lines, 'Xscript') + if RunVim([], [], '-u NONE -n -X -Z -e -m -s -S Xscript') + call assert_equal(['done'], readfile('Xresult')) + endif + + call delete('Xscript') + call delete('Xresult') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 82d12a04b4..9d5fbc2e39 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4327, /**/ 4326, /**/ From 73a16c22a4703cb9a7becdf459ce62bd894980d7 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 8 Feb 2022 17:40:36 +0000 Subject: [PATCH 14/19] patch 8.2.4328: command line complete matches cleard when typing character MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Command line complete matches cleard when typing character. (Dominique Pellé) Solution: Only remove a popup menu if there is one. --- src/ex_getln.c | 3 ++- src/testdir/dumps/Test_wildmenu_pum_30.dump | 10 ++++++++++ src/testdir/dumps/Test_wildmenu_pum_31.dump | 10 ++++++++++ src/testdir/test_cmdline.vim | 9 +++++++++ src/version.c | 2 ++ 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 src/testdir/dumps/Test_wildmenu_pum_30.dump create mode 100644 src/testdir/dumps/Test_wildmenu_pum_31.dump diff --git a/src/ex_getln.c b/src/ex_getln.c index 5def8a6a24..63f1c5dcbf 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -1883,7 +1883,8 @@ getcmdline_int( && c != Ctrl_L) { #ifdef FEAT_WILDMENU - cmdline_pum_remove(); + if (cmdline_pum_active()) + cmdline_pum_remove(); #endif (void)ExpandOne(&xpc, NULL, NULL, 0, WILD_FREE); did_wild_list = FALSE; diff --git a/src/testdir/dumps/Test_wildmenu_pum_30.dump b/src/testdir/dumps/Test_wildmenu_pum_30.dump new file mode 100644 index 0000000000..76e4780ea2 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_30.dump @@ -0,0 +1,10 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|c|n| @71 +|c|n|e|w|e|r| @6|c|n|f|i|l|e| @6|c|n|o|r|e|m|a|p| @40 +|c|n|e|x|t| @7|c|n|o|r|e|a|b@1|r|e|v| @1|c|n|o|r|e|m|e|n|u| @39 +|:|c|n> @71 diff --git a/src/testdir/dumps/Test_wildmenu_pum_31.dump b/src/testdir/dumps/Test_wildmenu_pum_31.dump new file mode 100644 index 0000000000..157f16c89c --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_31.dump @@ -0,0 +1,10 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|c|n| @71 +|c|n|e|w|e|r| @6|c|n|f|i|l|e| @6|c|n|o|r|e|m|a|p| @40 +|c|n|e|x|t| @7|c|n|o|r|e|a|b@1|r|e|v| @1|c|n|o|r|e|m|e|n|u| @39 +|:|c|n|s> @70 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 7faf811dbd..175647dd17 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -2132,6 +2132,15 @@ func Test_wildmenu_pum() call TermWait(buf) call VerifyScreenDump(buf, 'Test_wildmenu_pum_29', {}) + " Check "list" still works + call term_sendkeys(buf, "\set wildmode=longest,list\") + call term_sendkeys(buf, ":cn\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_30', {}) + call term_sendkeys(buf, "s") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_31', {}) + call term_sendkeys(buf, "\\") call StopVimInTerminal(buf) call delete('Xtest') diff --git a/src/version.c b/src/version.c index 9d5fbc2e39..7e61af9edd 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4328, /**/ 4327, /**/ From e023d499378942a6c3a3855cbe461ec2cb570f63 Mon Sep 17 00:00:00 2001 From: haya14busa Date: Tue, 8 Feb 2022 18:09:29 +0000 Subject: [PATCH 15/19] patch 8.2.4329: no support for end line number and column in 'errorformat' Problem: No support for end line number and column in 'errorformat'. Solution: Add %e and %k. (closes #9624) --- runtime/doc/quickfix.txt | 5 +++ src/quickfix.c | 76 ++++++++++++++++++++++++++--------- src/testdir/test_quickfix.vim | 23 +++++++++++ src/version.c | 2 + 4 files changed, 88 insertions(+), 18 deletions(-) diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index b7a01bdfd7..6bb259f723 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -1385,12 +1385,17 @@ Basic items %f file name (finds a string) %o module name (finds a string) %l line number (finds a number) + %e end line number (finds a number) %c column number (finds a number representing character column of the error, byte index, a is 1 character column) %v virtual column number (finds a number representing screen column of the error (1 == 8 screen columns)) + %k end column number (finds a number representing + the character column of the error, byte index, or a + number representing screen end column of the error if + it's used with %v) %t error type (finds a single character): e - error message w - warning message diff --git a/src/quickfix.c b/src/quickfix.c index 3643f27403..132d99216f 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -118,7 +118,7 @@ struct qf_info_S static qf_info_T ql_info; // global quickfix list static int_u last_qf_id = 0; // Last used quickfix list id -#define FMT_PATTERNS 11 // maximum number of % recognized +#define FMT_PATTERNS 13 // maximum number of % recognized /* * Structure used to hold the info of one part of 'errorformat' @@ -224,6 +224,9 @@ static bufref_T qf_last_bufref = {NULL, 0, 0}; */ #define LINE_MAXLEN 4096 +/* + * Patterns used. Keep in sync with qf_parse_fmt[]. + */ static struct fmtpattern { char_u convchar; @@ -231,16 +234,20 @@ static struct fmtpattern } fmt_pat[FMT_PATTERNS] = { {'f', ".\\+"}, // only used when at end - {'n', "\\d\\+"}, - {'l', "\\d\\+"}, - {'c', "\\d\\+"}, - {'t', "."}, - {'m', ".\\+"}, - {'r', ".*"}, - {'p', "[- .]*"}, - {'v', "\\d\\+"}, - {'s', ".\\+"}, - {'o', ".\\+"} + {'n', "\\d\\+"}, // 1 + {'l', "\\d\\+"}, // 2 + {'e', "\\d\\+"}, // 3 + {'c', "\\d\\+"}, // 4 + {'k', "\\d\\+"}, // 5 + {'t', "."}, // 6 +#define FMT_PATTERN_M 7 + {'m', ".\\+"}, // 7 +#define FMT_PATTERN_R 8 + {'r', ".*"}, // 8 + {'p', "[- .]*"}, // 9 + {'v', "\\d\\+"}, // 10 + {'s', ".\\+"}, // 11 + {'o', ".\\+"} // 12 }; /* @@ -265,9 +272,9 @@ efmpat_to_regpat( semsg(_(e_too_many_chr_in_format_string), *efmpat); return NULL; } - if ((idx && idx < 6 + if ((idx && idx < FMT_PATTERN_R && vim_strchr((char_u *)"DXOPQ", efminfo->prefix) != NULL) - || (idx == 6 + || (idx == FMT_PATTERN_R && vim_strchr((char_u *)"OPQ", efminfo->prefix) == NULL)) { semsg(_(e_unexpected_chr_in_format_str), *efmpat); @@ -948,7 +955,7 @@ qf_parse_fmt_n(regmatch_T *rmp, int midx, qffields_T *fields) } /* - * Parse the match for line number (%l') pattern in regmatch. + * Parse the match for line number ('%l') pattern in regmatch. * Return the matched value in "fields->lnum". */ static int @@ -960,6 +967,19 @@ qf_parse_fmt_l(regmatch_T *rmp, int midx, qffields_T *fields) return QF_OK; } +/* + * Parse the match for end line number ('%e') pattern in regmatch. + * Return the matched value in "fields->end_lnum". + */ + static int +qf_parse_fmt_e(regmatch_T *rmp, int midx, qffields_T *fields) +{ + if (rmp->startp[midx] == NULL) + return QF_FAIL; + fields->end_lnum = atol((char *)rmp->startp[midx]); + return QF_OK; +} + /* * Parse the match for column number ('%c') pattern in regmatch. * Return the matched value in "fields->col". @@ -973,6 +993,19 @@ qf_parse_fmt_c(regmatch_T *rmp, int midx, qffields_T *fields) return QF_OK; } +/* + * Parse the match for end column number ('%k') pattern in regmatch. + * Return the matched value in "fields->end_col". + */ + static int +qf_parse_fmt_k(regmatch_T *rmp, int midx, qffields_T *fields) +{ + if (rmp->startp[midx] == NULL) + return QF_FAIL; + fields->end_col = (int)atol((char *)rmp->startp[midx]); + return QF_OK; +} + /* * Parse the match for error type ('%t') pattern in regmatch. * Return the matched value in "fields->type". @@ -1132,16 +1165,19 @@ qf_parse_fmt_o(regmatch_T *rmp, int midx, qffields_T *fields) * 'errorformat' format pattern parser functions. * The '%f' and '%r' formats are parsed differently from other formats. * See qf_parse_match() for details. + * Keep in sync with fmt_pat[]. */ static int (*qf_parse_fmt[FMT_PATTERNS])(regmatch_T *, int, qffields_T *) = { - NULL, + NULL, // %f qf_parse_fmt_n, qf_parse_fmt_l, + qf_parse_fmt_e, qf_parse_fmt_c, + qf_parse_fmt_k, qf_parse_fmt_t, qf_parse_fmt_m, - NULL, + NULL, // %r qf_parse_fmt_p, qf_parse_fmt_v, qf_parse_fmt_s, @@ -1186,14 +1222,14 @@ qf_parse_match( midx = (int)fmt_ptr->addr[i]; if (i == 0 && midx > 0) // %f status = qf_parse_fmt_f(regmatch, midx, fields, idx); - else if (i == 5) + else if (i == FMT_PATTERN_M) { if (fmt_ptr->flags == '+' && !qf_multiscan) // %+ status = copy_nonerror_line(linebuf, linelen, fields); else if (midx > 0) // %m status = qf_parse_fmt_m(regmatch, midx, fields); } - else if (i == 6 && midx > 0) // %r + else if (i == FMT_PATTERN_R && midx > 0) // %r status = qf_parse_fmt_r(regmatch, midx, tail); else if (midx > 0) // others status = (qf_parse_fmt[i])(regmatch, midx, fields); @@ -1363,11 +1399,15 @@ qf_parse_multiline_pfx( if (!qfprev->qf_lnum) qfprev->qf_lnum = fields->lnum; + if (!qfprev->qf_end_lnum) + qfprev->qf_end_lnum = fields->end_lnum; if (!qfprev->qf_col) { qfprev->qf_col = fields->col; qfprev->qf_viscol = fields->use_viscol; } + if (!qfprev->qf_end_col) + qfprev->qf_end_col = fields->end_col; if (!qfprev->qf_fnum) qfprev->qf_fnum = qf_get_fnum(qfl, qfl->qf_directory, diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index adb0ea4fd5..7d2373b828 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -1469,6 +1469,29 @@ func Test_efm_error_type() let &efm = save_efm endfunc +" Test for end_lnum ('%e') and end_col ('%k') fields in 'efm' +func Test_efm_end_lnum_col() + let save_efm = &efm + + " single line + set efm=%f:%l-%e:%c-%k:%t:%m + cexpr ["Xfile1:10-20:1-2:E:msg1", "Xfile1:20-30:2-3:W:msg2",] + let output = split(execute('clist'), "\n") + call assert_equal([ + \ ' 1 Xfile1:10-20 col 1-2 error: msg1', + \ ' 2 Xfile1:20-30 col 2-3 warning: msg2'], output) + + " multiple lines + set efm=%A%n)%m,%Z%f:%l-%e:%c-%k + cexpr ["1)msg1", "Xfile1:14-24:1-2", + \ "2)msg2", "Xfile1:24-34:3-4"] + let output = split(execute('clist'), "\n") + call assert_equal([ + \ ' 1 Xfile1:14-24 col 1-2 error 1: msg1', + \ ' 2 Xfile1:24-34 col 3-4 error 2: msg2'], output) + let &efm = save_efm +endfunc + func XquickfixChangedByAutocmd(cchar) call s:setup_commands(a:cchar) if a:cchar == 'c' diff --git a/src/version.c b/src/version.c index 7e61af9edd..2feed3cf37 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4329, /**/ 4328, /**/ From 779aeff5c32c26161f42cdccbaa2376e78ee77d6 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 8 Feb 2022 19:12:19 +0000 Subject: [PATCH 16/19] patch 8.2.4330: Vim9: no error if script imports itself Problem: Vim9: no error if script imports itself. Solution: Give an error when a script imports itself. --- src/errors.h | 3 ++- src/testdir/test_vim9_import.vim | 11 ++++++++++- src/version.c | 2 ++ src/vim9script.c | 6 ++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/errors.h b/src/errors.h index ecd4eb92d0..c66d26d8d9 100644 --- a/src/errors.h +++ b/src/errors.h @@ -2802,7 +2802,8 @@ EXTERN char e_function_reference_invalid[] INIT(= N_("E1086: Function reference invalid")); EXTERN char e_cannot_use_index_when_declaring_variable[] INIT(= N_("E1087: Cannot use an index when declaring a variable")); -// E1088 unused +EXTERN char e_script_cannot_import_itself[] + INIT(= N_("E1088: Script cannot import itself")); EXTERN char e_unknown_variable_str[] INIT(= N_("E1089: Unknown variable: %s")); EXTERN char e_cannot_assign_to_argument[] diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim index 1e8851e92d..652e4d36b6 100644 --- a/src/testdir/test_vim9_import.vim +++ b/src/testdir/test_vim9_import.vim @@ -500,7 +500,16 @@ def Test_import_fails() v9.CheckScriptFailure(lines, 'E1262:') delete('Xthat.vim') - + + lines =<< trim END + vim9script + export var item = 'hello' + import './Xyourself.vim' + END + writefile(lines, 'Xyourself.vim') + assert_fails('source Xyourself.vim', 'E1088:') + delete('Xyourself.vim') + mkdir('Ximport') writefile(['vim9script'], 'Ximport/.vim') diff --git a/src/version.c b/src/version.c index 2feed3cf37..d7da6df6e0 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4330, /**/ 4329, /**/ diff --git a/src/vim9script.c b/src/vim9script.c index 5c1c055668..872108827e 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -516,6 +516,12 @@ handle_import( goto erret; } + if (sid == current_sctx.sc_sid) + { + emsg(_(e_script_cannot_import_itself)); + goto erret; + } + import_gap = gap != NULL ? gap : &SCRIPT_ITEM(import_sid)->sn_imports; for (i = 0; i < import_gap->ga_len; ++i) { From 3a5988c025f8517ba382730dc54bb13df937edb4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 8 Feb 2022 19:23:35 +0000 Subject: [PATCH 17/19] patch 8.2.4331: Vim9: no test for existing script variable in block Problem: Vim9: no test for existing script variable in block. Solution: Add a test. --- src/testdir/test_vim9_func.vim | 26 +++++++++++++++++++++++++- src/version.c | 2 ++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index ebcd0fbc8e..e68cbcf152 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -1028,11 +1028,35 @@ def Test_call_wrong_args() vim9script var name = 'piet' def FuncOne(name: string) - echo nr + echo name enddef END v9.CheckScriptFailure(lines, 'E1168:') + # same, inside the same block + lines =<< trim END + vim9script + if true + var name = 'piet' + def FuncOne(name: string) + echo name + enddef + endif + END + v9.CheckScriptFailure(lines, 'E1168:') + + # variable in other block is OK + lines =<< trim END + vim9script + if true + var name = 'piet' + endif + def FuncOne(name: string) + echo name + enddef + END + v9.CheckScriptSuccess(lines) + # argument name declared later is only found when compiling lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c index d7da6df6e0..e0bb6e14bf 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4331, /**/ 4330, /**/ From dce2441a603f2c9343a4a46091283a32420d80a2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 8 Feb 2022 20:35:30 +0000 Subject: [PATCH 18/19] patch 8.2.4332: Vim9: incomplete test for existing script variable in block Problem: Vim9: incomplete test for existing script variable in block. Solution: Add a couple more tests. Fix uncovered problem. --- src/proto/vim9compile.pro | 4 +-- src/testdir/test_vim9_func.vim | 37 ++++++++++++++++++++++++ src/userfunc.c | 11 +++++--- src/version.c | 2 ++ src/vim9compile.c | 51 +++++++++++++++++++++++----------- src/vim9expr.c | 2 +- src/vim9script.c | 3 +- 7 files changed, 86 insertions(+), 24 deletions(-) diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro index b7f7539215..0790decb8a 100644 --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -2,8 +2,8 @@ int lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx); int arg_exists(char_u *name, size_t len, int *idxp, type_T **type, int *gen_load_outer, cctx_T *cctx); int script_is_vim9(void); -int script_var_exists(char_u *name, size_t len, cctx_T *cctx); -int check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg); +int script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack); +int check_defined(char_u *p, size_t len, cctx_T *cctx, cstack_T *cstack, int is_arg); int need_type_where(type_T *actual, type_T *expected, int offset, where_T where, cctx_T *cctx, int silent, int actual_is_const); int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const); lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type); diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index e68cbcf152..46e562d1ed 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -1057,6 +1057,43 @@ def Test_call_wrong_args() END v9.CheckScriptSuccess(lines) + # with another variable in another block + lines =<< trim END + vim9script + if true + var name = 'piet' + # define a function so that the variable isn't cleared + def GetItem(): string + return item + enddef + endif + if true + var name = 'peter' + def FuncOne(name: string) + echo name + enddef + endif + END + v9.CheckScriptFailure(lines, 'E1168:') + + # only variable in another block is OK + lines =<< trim END + vim9script + if true + var name = 'piet' + # define a function so that the variable isn't cleared + def GetItem(): string + return item + enddef + endif + if true + def FuncOne(name: string) + echo name + enddef + endif + END + v9.CheckScriptSuccess(lines) + # argument name declared later is only found when compiling lines =<< trim END vim9script diff --git a/src/userfunc.c b/src/userfunc.c index 0c54e35749..59415dbd73 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -55,6 +55,7 @@ func_tbl_get(void) * If "argtypes" is not NULL also get the type: "arg: type" (:def function). * If "types_optional" is TRUE a missing type is OK, use "any". * If "evalarg" is not NULL use it to check for an already declared name. + * If "eap" is not NULL use it to check for an already declared name. * Return a pointer to after the type. * When something is wrong return "arg". */ @@ -65,6 +66,7 @@ one_function_arg( garray_T *argtypes, int types_optional, evalarg_T *evalarg, + exarg_T *eap, int is_vararg, int skip) { @@ -87,7 +89,8 @@ one_function_arg( // Vim9 script: cannot use script var name for argument. In function: also // check local vars and arguments. if (!skip && argtypes != NULL && check_defined(arg, p - arg, - evalarg == NULL ? NULL : evalarg->eval_cctx, TRUE) == FAIL) + evalarg == NULL ? NULL : evalarg->eval_cctx, + eap == NULL ? NULL : eap->cstack, TRUE) == FAIL) return arg; if (newargs != NULL && ga_grow(newargs, 1) == FAIL) @@ -210,7 +213,7 @@ get_function_args( int *varargs, garray_T *default_args, int skip, - exarg_T *eap, + exarg_T *eap, // can be NULL garray_T *lines_to_free) { int mustend = FALSE; @@ -279,7 +282,7 @@ get_function_args( arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, - evalarg, TRUE, skip); + evalarg, eap, TRUE, skip); if (p == arg) break; if (*skipwhite(p) == '=') @@ -295,7 +298,7 @@ get_function_args( arg = p; p = one_function_arg(p, newargs, argtypes, types_optional, - evalarg, FALSE, skip); + evalarg, eap, FALSE, skip); if (p == arg) break; diff --git a/src/version.c b/src/version.c index e0bb6e14bf..9f3dfd2f65 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4332, /**/ 4331, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index debead4181..5f80988b80 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -152,11 +152,12 @@ arg_exists( * Lookup a script-local variable in the current script, possibly defined in a * block that contains the function "cctx->ctx_ufunc". * "cctx" is NULL at the script level. + * "cstack_T" is NULL in a function. * If "len" is <= 0 "name" must be NUL terminated. * Return NULL when not found. */ static sallvar_T * -find_script_var(char_u *name, size_t len, cctx_T *cctx) +find_script_var(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); hashitem_T *hi; @@ -183,11 +184,22 @@ find_script_var(char_u *name, size_t len, cctx_T *cctx) if (cctx == NULL) { - // Not in a function scope, find variable with block id equal to or - // smaller than the current block id. + // Not in a function scope, find variable with block ID equal to or + // smaller than the current block id. If "cstack" is not NULL go up + // the block scopes (more accurate). while (sav != NULL) { - if (sav->sav_block_id <= si->sn_current_block_id) + if (cstack != NULL) + { + int idx; + + for (idx = cstack->cs_idx; idx >= 0; --idx) + if (cstack->cs_block_id[idx] == sav->sav_block_id) + break; + if (idx >= 0) + break; + } + else if (sav->sav_block_id <= si->sn_current_block_id) break; sav = sav->sav_next; } @@ -225,10 +237,11 @@ script_is_vim9() /* * Lookup a variable (without s: prefix) in the current script. * "cctx" is NULL at the script level. + * "cstack" is NULL in a function. * Returns OK or FAIL. */ int -script_var_exists(char_u *name, size_t len, cctx_T *cctx) +script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack) { if (current_sctx.sc_sid <= 0) return FAIL; @@ -236,7 +249,7 @@ script_var_exists(char_u *name, size_t len, cctx_T *cctx) { // Check script variables that were visible where the function was // defined. - if (find_script_var(name, len, cctx) != NULL) + if (find_script_var(name, len, cctx, cstack) != NULL) return OK; } else @@ -267,7 +280,7 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx) return (cctx != NULL && (lookup_local(name, len, NULL, cctx) == OK || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK)) - || script_var_exists(name, len, cctx) == OK + || script_var_exists(name, len, cctx, NULL) == OK || find_imported(name, len, FALSE, cctx) != NULL; } @@ -309,7 +322,12 @@ item_exists(char_u *name, size_t len, int cmd UNUSED, cctx_T *cctx) * Return FAIL and give an error if it defined. */ int -check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg) +check_defined( + char_u *p, + size_t len, + cctx_T *cctx, + cstack_T *cstack, + int is_arg) { int c = p[len]; ufunc_T *ufunc = NULL; @@ -318,7 +336,7 @@ check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg) if (len == 1 && *p == '_') return OK; - if (script_var_exists(p, len, cctx) == OK) + if (script_var_exists(p, len, cctx, cstack) == OK) { if (is_arg) semsg(_(e_argument_already_declared_in_script_str), p); @@ -526,7 +544,7 @@ get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx) return -1; if (sid == current_sctx.sc_sid) { - sallvar_T *sav = find_script_var(name, 0, cctx); + sallvar_T *sav = find_script_var(name, 0, cctx, NULL); if (sav == NULL) return -2; @@ -884,7 +902,8 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free) semsg(_(e_namespace_not_supported_str), name_start); return NULL; } - if (check_defined(name_start, name_end - name_start, cctx, FALSE) == FAIL) + if (check_defined(name_start, name_end - name_start, cctx, + NULL, FALSE) == FAIL) return NULL; if (!ASCII_ISUPPER(is_global ? name_start[2] : name_start[0])) { @@ -1356,9 +1375,9 @@ compile_lhs( && STRNCMP(var_start, "s:", 2) == 0; int script_var = (script_namespace ? script_var_exists(var_start + 2, lhs->lhs_varlen - 2, - cctx) + cctx, NULL) : script_var_exists(var_start, lhs->lhs_varlen, - cctx)) == OK; + cctx, NULL)) == OK; imported_T *import = find_imported(var_start, lhs->lhs_varlen, FALSE, cctx); @@ -1442,8 +1461,8 @@ compile_lhs( } } } - else if (check_defined(var_start, lhs->lhs_varlen, cctx, FALSE) - == FAIL) + else if (check_defined(var_start, lhs->lhs_varlen, cctx, + NULL, FALSE) == FAIL) return FAIL; } } @@ -2470,7 +2489,7 @@ check_args_shadowing(ufunc_T *ufunc, cctx_T *cctx) for (i = 0; i < ufunc->uf_args.ga_len; ++i) { arg = ((char_u **)(ufunc->uf_args.ga_data))[i]; - if (check_defined(arg, STRLEN(arg), cctx, TRUE) == FAIL) + if (check_defined(arg, STRLEN(arg), cctx, NULL, TRUE) == FAIL) { r = FAIL; break; diff --git a/src/vim9expr.c b/src/vim9expr.c index 402803768b..b44f9288d9 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -501,7 +501,7 @@ compile_load( { // "var" can be script-local even without using "s:" if it // already exists in a Vim9 script or when it's imported. - if (script_var_exists(*arg, len, cctx) == OK + if (script_var_exists(*arg, len, cctx, NULL) == OK || find_imported(name, 0, FALSE, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); diff --git a/src/vim9script.c b/src/vim9script.c index 872108827e..1b8988d55f 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -600,7 +600,8 @@ handle_import( goto erret; } else if (imported == NULL - && check_defined(as_name, STRLEN(as_name), cctx, FALSE) == FAIL) + && check_defined(as_name, STRLEN(as_name), cctx, NULL, + FALSE) == FAIL) goto erret; if (imported == NULL) From b6a138eb334621f60c5891d035f80f398d59dbd3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 8 Feb 2022 21:17:22 +0000 Subject: [PATCH 19/19] patch 8.2.4333: cstack not always passed to where it is needed Problem: cstack not always passed to where it is needed. Solution: Pass ctack through functions. --- src/eval.c | 4 ++-- src/proto/vim9compile.pro | 2 +- src/proto/vim9script.pro | 2 +- src/version.c | 2 ++ src/vim9compile.c | 11 ++++++++--- src/vim9expr.c | 4 ++-- src/vim9script.c | 4 +++- 7 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/eval.c b/src/eval.c index 2942d0fe1d..d775ee3f26 100644 --- a/src/eval.c +++ b/src/eval.c @@ -975,7 +975,7 @@ get_lval( cc = *p; *p = NUL; if (find_exported(import->imp_sid, lp->ll_name, &ufunc, &type, - NULL, TRUE) == -1) + NULL, NULL, TRUE) == -1) { *p = cc; return NULL; @@ -6056,7 +6056,7 @@ handle_subscript( **arg = NUL; idx = find_exported(rettv->vval.v_number, exp_name, &ufunc, &type, - evalarg->eval_cctx, verbose); + evalarg->eval_cctx, evalarg->eval_cstack, verbose); **arg = cc; if (idx < 0 && ufunc == NULL) diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro index 0790decb8a..9effb1744b 100644 --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -7,7 +7,7 @@ int check_defined(char_u *p, size_t len, cctx_T *cctx, cstack_T *cstack, int is_ int need_type_where(type_T *actual, type_T *expected, int offset, where_T where, cctx_T *cctx, int silent, int actual_is_const); int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const); lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type); -int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx); +int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx, cstack_T *cstack); imported_T *find_imported(char_u *name, size_t len, int load, cctx_T *cctx); char_u *may_peek_next_line(cctx_T *cctx, char_u *arg, char_u **nextp); char_u *peek_next_line_from_context(cctx_T *cctx); diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro index 46211ddb09..6295dc2cca 100644 --- a/src/proto/vim9script.pro +++ b/src/proto/vim9script.pro @@ -11,7 +11,7 @@ void ex_export(exarg_T *eap); void free_imports_and_script_vars(int sid); void mark_imports_for_reload(int sid); void ex_import(exarg_T *eap); -int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, int verbose); +int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, cstack_T *cstack, int verbose); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); void update_vim9_script_var(int create, dictitem_T *di, char_u *name, int flags, typval_T *tv, type_T **type, int do_member); void hide_script_var(scriptitem_T *si, int idx, int func_defined); diff --git a/src/version.c b/src/version.c index 9f3dfd2f65..a4480e1866 100644 --- a/src/version.c +++ b/src/version.c @@ -746,6 +746,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4333, /**/ 4332, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 5f80988b80..265ea665b8 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -532,7 +532,12 @@ check_item_writable(svar_T *sv, int check_writable, char_u *name) * If not found or the variable is not writable returns -2. */ int -get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx) +get_script_item_idx( + int sid, + char_u *name, + int check_writable, + cctx_T *cctx, + cstack_T *cstack) { hashtab_T *ht; dictitem_T *di; @@ -544,7 +549,7 @@ get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx) return -1; if (sid == current_sctx.sc_sid) { - sallvar_T *sav = find_script_var(name, 0, cctx, NULL); + sallvar_T *sav = find_script_var(name, 0, cctx, cstack); if (sav == NULL) return -2; @@ -1449,7 +1454,7 @@ compile_lhs( lhs->lhs_scriptvar_idx = get_script_item_idx( lhs->lhs_scriptvar_sid, rawname, lhs->lhs_has_index ? ASSIGN_FINAL : ASSIGN_CONST, - cctx); + cctx, NULL); if (lhs->lhs_scriptvar_idx >= 0) { scriptitem_T *si = SCRIPT_ITEM( diff --git a/src/vim9expr.c b/src/vim9expr.c index b44f9288d9..1e53478a50 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -256,7 +256,7 @@ compile_load_scriptvar( if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) return FAIL; si = SCRIPT_ITEM(current_sctx.sc_sid); - idx = get_script_item_idx(current_sctx.sc_sid, name, 0, cctx); + idx = get_script_item_idx(current_sctx.sc_sid, name, 0, cctx, NULL); if (idx >= 0) { svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; @@ -316,7 +316,7 @@ compile_load_scriptvar( else { idx = find_exported(import->imp_sid, exp_name, &ufunc, &type, - cctx, TRUE); + cctx, NULL, TRUE); } *p = cc; *end = p; diff --git a/src/vim9script.c b/src/vim9script.c index 1b8988d55f..fde51c91f9 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -649,6 +649,7 @@ ex_import(exarg_T *eap) /* * Find an exported item in "sid" matching "name". + * Either "cctx" or "cstack" is NULL. * When it is a variable return the index. * When it is a user function return "*ufunc". * When not found returns -1 and "*ufunc" is NULL. @@ -660,6 +661,7 @@ find_exported( ufunc_T **ufunc, type_T **type, cctx_T *cctx, + cstack_T *cstack, int verbose) { int idx = -1; @@ -667,7 +669,7 @@ find_exported( scriptitem_T *script = SCRIPT_ITEM(sid); // Find name in "script". - idx = get_script_item_idx(sid, name, 0, cctx); + idx = get_script_item_idx(sid, name, 0, cctx, cstack); if (idx >= 0) { sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;