From a991ce9c083bb8c02b1b1ec34ed35728197050f3 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 6 Oct 2023 19:16:36 +0200 Subject: [PATCH 01/47] patch 9.0.1995: Invalid memory access with empty 'foldexpr' Problem: Invalid memory access when 'foldexpr' returns empty string. Solution: Check for NUL. closes: #13293 Signed-off-by: Christian Brabandt Co-authored-by: zeertzjq --- src/eval.c | 2 +- src/testdir/test_fold.vim | 9 +++++++++ src/version.c | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/eval.c b/src/eval.c index 19ab01561d..a9f7112f2d 100644 --- a/src/eval.c +++ b/src/eval.c @@ -968,7 +968,7 @@ eval_foldexpr(win_T *wp, int *cp) // If the result is a string, check if there is a non-digit before // the number. s = tv.vval.v_string; - if (!VIM_ISDIGIT(*s) && *s != '-') + if (*s != NUL && !VIM_ISDIGIT(*s) && *s != '-') *cp = *s++; retval = atol((char *)s); } diff --git a/src/testdir/test_fold.vim b/src/testdir/test_fold.vim index 398a0c2d71..cb29d43d54 100644 --- a/src/testdir/test_fold.vim +++ b/src/testdir/test_fold.vim @@ -1769,4 +1769,13 @@ func Test_foldcolumn_linebreak_control_char() bwipe! endfunc +" This used to cause invalid memory access +func Test_foldexpr_return_empty_string() + new + setlocal foldexpr='' foldmethod=expr + redraw + + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 057cae580a..ce13dcc776 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1995, /**/ 1994, /**/ From 26e8f7b0ab451e76319d113038c86c79488bbbc4 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan <4298407+yegappan@users.noreply.github.com> Date: Fri, 6 Oct 2023 10:24:10 -0700 Subject: [PATCH 02/47] runtime(doc): Update vim9class help (#13292) Signed-off-by: Christian Brabandt --- runtime/doc/vim9class.txt | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt index 3e5493f203..00bdf369eb 100644 --- a/runtime/doc/vim9class.txt +++ b/runtime/doc/vim9class.txt @@ -413,7 +413,9 @@ prefix when defining the method: > *E1373* A class extending the abstract class must implement all the abstract methods. The signature (arguments, argument types and return type) must be exactly the -same. Class methods in an abstract class can also be abstract methods. +same. If the return type of a method is a class, then that class or one of +its subclasses can be used in the extended method. Class methods in an +abstract class can also be abstract methods. ============================================================================== @@ -548,8 +550,10 @@ is not possible to override them (unlike some other languages). *E1356* *E1357* *E1358* Object methods of the base class can be overruled. The signature (arguments, -argument types and return type) must be exactly the same. The method of the -base class can be called by prefixing "super.". +argument types and return type) must be exactly the same. If the return type +of a method is a class, then that class or one of its subclasses can be used +in the extended method. The method of the base class can be called by +prefixing "super.". *E1377* The access level of a method (public or private) in a child class should be @@ -738,17 +742,19 @@ constructor methods. 7. Type definition *Vim9-type* *:type* +{not implemented yet} + A type definition is giving a name to a type specification. For Example: > :type ListOfStrings list -TODO: more explanation - ============================================================================== 8. Enum *Vim9-enum* *:enum* *:endenum* +{not implemented yet} + An enum is a type that can have one of a list of values. Example: > :enum Color @@ -759,8 +765,6 @@ An enum is a type that can have one of a list of values. Example: > Black :endenum -TODO: more explanation - ============================================================================== From fa145f200966e47e11c403520374d6d37cfd1de7 Mon Sep 17 00:00:00 2001 From: Ken Takata Date: Fri, 6 Oct 2023 19:27:13 +0200 Subject: [PATCH 03/47] patch 9.0.1996: Cannot build with python312 Problem: Cannot build with python312 Solution: Define wrapper types and functions for python 3.12 Py_SIZE() uses PyLong_Type and PyBool_Type starting from Python 3.12. We need to define our own Py_SIZE() to replace Py{Bool,Long}_Type with py3_Py{Bool,Long}_Type. We also need to redefine PyTuple_GET_SIZE() and PyList_GET_SIZE(), because they use Py_SIZE(). closes: #13281 closes: #13290 Signed-off-by: Christian Brabandt Co-authored-by: Ken Takata --- src/if_python3.c | 49 +++++++++++++++++++++++++++++++++++++++++++----- src/version.c | 2 ++ 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/if_python3.c b/src/if_python3.c index bbbebc39f1..52027bd562 100644 --- a/src/if_python3.c +++ b/src/if_python3.c @@ -68,8 +68,6 @@ #endif #define PY_SSIZE_T_CLEAN -#define PyLong_Type (*py3_PyLong_Type) -#define PyBool_Type (*py3_PyBool_Type) #ifdef Py_LIMITED_API # define USE_LIMITED_API // Using Python 3 limited ABI @@ -297,6 +295,10 @@ static HINSTANCE hinstPy3 = 0; // Instance of python.dll # define PyFloat_Type (*py3_PyFloat_Type) # define PyNumber_Check (*py3_PyNumber_Check) # define PyNumber_Long (*py3_PyNumber_Long) +# define PyBool_Type (*py3_PyBool_Type) +# if PY_VERSION_HEX >= 0x030c00b0 +# define PyLong_Type (*py3_PyLong_Type) +# endif # define PyErr_NewException py3_PyErr_NewException # ifdef Py_DEBUG # define _Py_NegativeRefcount py3__Py_NegativeRefcount @@ -496,9 +498,9 @@ static PyTypeObject* py3_PyStdPrinter_Type; # endif static PyTypeObject* py3_PySlice_Type; static PyTypeObject* py3_PyFloat_Type; -PyTypeObject* py3_PyBool_Type; +static PyTypeObject* py3_PyBool_Type; # if PY_VERSION_HEX >= 0x030c00b0 -PyTypeObject* py3_PyLong_Type; +static PyTypeObject* py3_PyLong_Type; # endif static int (*py3_PyNumber_Check)(PyObject *); static PyObject* (*py3_PyNumber_Long)(PyObject *); @@ -696,8 +698,9 @@ static struct # endif {"PySlice_Type", (PYTHON_PROC*)&py3_PySlice_Type}, {"PyFloat_Type", (PYTHON_PROC*)&py3_PyFloat_Type}, -# if PY_VERSION_HEX < 0x030c00b0 {"PyBool_Type", (PYTHON_PROC*)&py3_PyBool_Type}, +# if PY_VERSION_HEX >= 0x030c00b0 + {"PyLong_Type", (PYTHON_PROC*)&py3_PyLong_Type}, # endif {"PyNumber_Check", (PYTHON_PROC*)&py3_PyNumber_Check}, {"PyNumber_Long", (PYTHON_PROC*)&py3_PyNumber_Long}, @@ -789,6 +792,42 @@ py3__PyObject_TypeCheck(PyObject *ob, PyTypeObject *type) # endif # endif +# if !defined(USE_LIMITED_API) && PY_VERSION_HEX >= 0x030c00b0 +// Py_SIZE() uses PyLong_Type and PyBool_Type starting from Python 3.12. +// We need to define our own Py_SIZE() to replace Py{Bool,Long}_Type with +// py3_Py{Bool,Long}_Type. +// We also need to redefine PyTuple_GET_SIZE() and PyList_GET_SIZE(), because +// they use Py_SIZE(). + static inline Py_ssize_t +py3_Py_SIZE(PyObject *ob) +{ + assert(ob->ob_type != &PyLong_Type); + assert(ob->ob_type != &PyBool_Type); + PyVarObject *var_ob = _PyVarObject_CAST(ob); + return var_ob->ob_size; +} +# undef Py_SIZE +# define Py_SIZE(ob) py3_Py_SIZE(_PyObject_CAST(ob)) + + static inline Py_ssize_t +py3_PyTuple_GET_SIZE(PyObject *op) +{ + PyTupleObject *tuple = _PyTuple_CAST(op); + return Py_SIZE(tuple); +} +# undef PyTuple_GET_SIZE +# define PyTuple_GET_SIZE(op) py3_PyTuple_GET_SIZE(_PyObject_CAST(op)) + + static inline +Py_ssize_t py3_PyList_GET_SIZE(PyObject *op) +{ + PyListObject *list = _PyList_CAST(op); + return Py_SIZE(list); +} +# undef PyList_GET_SIZE +# define PyList_GET_SIZE(op) py3_PyList_GET_SIZE(_PyObject_CAST(op)) +# endif + # ifdef MSWIN /* * Look up the library "libname" using the InstallPath registry key. diff --git a/src/version.c b/src/version.c index ce13dcc776..ec25a1213d 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1996, /**/ 1995, /**/ From 27e12c7669e36a8f60fefa9db9a08024efeb06e8 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 7 Oct 2023 01:34:04 +0800 Subject: [PATCH 04/47] runtime(doc): remove E1520 tag (#13289) Signed-off-by: Christian Brabandt --- runtime/doc/builtin.txt | 12 +++++------- runtime/doc/tags | 1 - 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index a4eb95e441..029c08bbd9 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -6690,8 +6690,6 @@ printf({fmt}, {expr1} ...) *printf()* precision, the argument(s) to be used must also be specified using a {n$} positional argument specifier. See |printf-$|. - - *E1520* The conversion specifiers and their meanings are: *printf-d* *printf-b* *printf-B* *printf-o* @@ -6885,11 +6883,11 @@ printf({fmt}, {expr1} ...) *printf()* %1$d at width %2$d is: %01$*2$.3$d *E1507* - This internal error indicates that the logic to - parse a positional format error ran into a problem - that couldn't be otherwise reported. Please file a - bug against vim if you run into this, copying the - exact format string and parameters that were used. + This internal error indicates that the logic to parse a + positional format argument ran into a problem that couldn't be + otherwise reported. Please file a bug against Vim if you run + into this, copying the exact format string and parameters that + were used. prompt_getprompt({buf}) *prompt_getprompt()* diff --git a/runtime/doc/tags b/runtime/doc/tags index ca4eaf9043..77b7d1b2de 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -4515,7 +4515,6 @@ E1508 editing.txt /*E1508* E1509 editing.txt /*E1509* E151 helphelp.txt /*E151* E152 helphelp.txt /*E152* -E1520 builtin.txt /*E1520* E153 helphelp.txt /*E153* E154 helphelp.txt /*E154* E155 sign.txt /*E155* From 580c1fcb4ad85360cd3a361c3c8e37b534153d60 Mon Sep 17 00:00:00 2001 From: dundargoc Date: Fri, 6 Oct 2023 19:41:14 +0200 Subject: [PATCH 05/47] patch 9.0.1997: Some unused code in move.c and string.c Problem: Some unused code in move.c and string.c Solution: Remove it closes: #13288 Signed-off-by: Christian Brabandt Co-authored-by: dundargoc --- src/move.c | 6 ------ src/strings.c | 6 +++--- src/version.c | 2 ++ 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/move.c b/src/move.c index 46e4f358dc..a0d3a6014a 100644 --- a/src/move.c +++ b/src/move.c @@ -1753,10 +1753,7 @@ scrolldown( ++row; } if (col > width2 && width2 > 0) - { row += col / width2; - col = col % width2; - } if (row >= curwin->w_height) { curwin->w_curswant = curwin->w_virtcol @@ -1989,10 +1986,7 @@ adjust_skipcol(void) ++row; } if (col > width2) - { row += col / width2; - col = col % width2; - } if (row >= curwin->w_height) { if (curwin->w_skipcol == 0) diff --git a/src/strings.c b/src/strings.c index 33616d609b..c04cbe84f6 100644 --- a/src/strings.c +++ b/src/strings.c @@ -2596,7 +2596,7 @@ parse_fmt_types( CHECK_POS_ARG; } } - else if (VIM_ISDIGIT((int)(*(arg = p)))) + else if (VIM_ISDIGIT((int)(*p))) { // size_t could be wider than unsigned int; make sure we treat // argument like common implementations do @@ -2651,7 +2651,7 @@ parse_fmt_types( CHECK_POS_ARG; } } - else if (VIM_ISDIGIT((int)(*(arg = p)))) + else if (VIM_ISDIGIT((int)(*p))) { // size_t could be wider than unsigned int; make sure we // treat argument like common implementations do @@ -2684,7 +2684,7 @@ parse_fmt_types( if (length_modifier == 'l' && *p == 'l') { // double l = __int64 / varnumber_T - length_modifier = 'L'; + // length_modifier = 'L'; p++; } } diff --git a/src/version.c b/src/version.c index ec25a1213d..a946279ec2 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1997, /**/ 1996, /**/ From 85f4521808dd9a587c00f9a2927e84217721cfca Mon Sep 17 00:00:00 2001 From: tristhaus Date: Fri, 6 Oct 2023 19:51:13 +0200 Subject: [PATCH 06/47] patch 9.0.1998: xxd: cannot reverse a bit dump Problem: xxd: cannot reverse a bit dump Solution: implement reversing the bit dump using -b -r closes: #13286 Signed-off-by: Christian Brabandt Co-authored-by: tristhaus --- runtime/doc/xxd-fr.1 | 2 +- runtime/doc/xxd-fr.UTF-8.1 | 2 +- runtime/doc/xxd-it.1 | 2 +- runtime/doc/xxd-it.UTF-8.1 | 2 +- runtime/doc/xxd-ja.UTF-8.1 | 2 +- runtime/doc/xxd-pl.1 | 2 +- runtime/doc/xxd-pl.UTF-8.1 | 2 +- runtime/doc/xxd-ru.1 | 2 +- runtime/doc/xxd-ru.UTF-8.1 | 2 +- runtime/doc/xxd.1 | 6 +- runtime/doc/xxd.man | 5 +- src/testdir/test_xxd.vim | 37 ++++++++++ src/version.c | 2 + src/xxd/xxd.c | 143 +++++++++++++++++++++++++++---------- 14 files changed, 161 insertions(+), 50 deletions(-) diff --git a/runtime/doc/xxd-fr.1 b/runtime/doc/xxd-fr.1 index e170df5d77..7aa3ff1a67 100644 --- a/runtime/doc/xxd-fr.1 +++ b/runtime/doc/xxd-fr.1 @@ -70,7 +70,7 @@ Convertit en binaires plut Cette option crit les octets comme une squence de "1" et de "0" au lieu d'une conversion en hexadcimal traditionnel. Chaque ligne est prcde par un numro de ligne en hexadcimal et suivie de la reprsentation ASCII (ou -EBCDIC) correspondante. Les options \-r, \-p, \-i ne fonctionnent pas dans ce +EBCDIC) correspondante. Les options \-p, \-i ne fonctionnent pas dans ce mode. .TP .IR "\-c cols " | " \-cols cols" diff --git a/runtime/doc/xxd-fr.UTF-8.1 b/runtime/doc/xxd-fr.UTF-8.1 index 47773a1363..9c4c4c928a 100644 --- a/runtime/doc/xxd-fr.UTF-8.1 +++ b/runtime/doc/xxd-fr.UTF-8.1 @@ -70,7 +70,7 @@ Convertit en binaires plutôt qu'en hexadécimal. Cette option écrit les octets comme une séquence de "1" et de "0" au lieu d'une conversion en hexadécimal traditionnel. Chaque ligne est précédée par un numéro de ligne en hexadécimal et suivie de la représentation ASCII (ou -EBCDIC) correspondante. Les options \-r, \-p, \-i ne fonctionnent pas dans ce +EBCDIC) correspondante. Les options \-p, \-i ne fonctionnent pas dans ce mode. .TP .IR "\-c cols " | " \-cols cols" diff --git a/runtime/doc/xxd-it.1 b/runtime/doc/xxd-it.1 index 9034fddc36..ed4fd81946 100644 --- a/runtime/doc/xxd-it.1 +++ b/runtime/doc/xxd-it.1 @@ -63,7 +63,7 @@ Richiesta di omissione: Un singolo '*' rimpiazza righe a zeri binari. Default: o Richiesta di un'immagine binaria (cifre binarie), invece che esadecimale. Quest'opzione scrive un byte come otto cifre "1" e "0" invece di usare i numeri esadecimali. Ogni riga preceduta da un indirizzo in esadecimale e -seguita da una decodifica ASCII (o EBCDIC). Le opzioni \-r, \-p, \-i, +seguita da una decodifica ASCII (o EBCDIC). Le opzioni \-p, \-i, specificabili dalla riga comando, non funzionano in questo modo. .TP .IR "\-c colonne " | " \-cols colonne" diff --git a/runtime/doc/xxd-it.UTF-8.1 b/runtime/doc/xxd-it.UTF-8.1 index b982a2c9d3..8653de972f 100644 --- a/runtime/doc/xxd-it.UTF-8.1 +++ b/runtime/doc/xxd-it.UTF-8.1 @@ -63,7 +63,7 @@ Richiesta di omissione: Un singolo '*' rimpiazza righe a zeri binari. Default: o Richiesta di un'immagine binaria (cifre binarie), invece che esadecimale. Quest'opzione scrive un byte come otto cifre "1" e "0" invece di usare i numeri esadecimali. Ogni riga è preceduta da un indirizzo in esadecimale e -seguita da una decodifica ASCII (o EBCDIC). Le opzioni \-r, \-p, \-i, +seguita da una decodifica ASCII (o EBCDIC). Le opzioni \-p, \-i, specificabili dalla riga comando, non funzionano in questo modo. .TP .IR "\-c colonne " | " \-cols colonne" diff --git a/runtime/doc/xxd-ja.UTF-8.1 b/runtime/doc/xxd-ja.UTF-8.1 index a03671cb1d..1e06bde814 100644 --- a/runtime/doc/xxd-ja.UTF-8.1 +++ b/runtime/doc/xxd-ja.UTF-8.1 @@ -58,7 +58,7 @@ 1 オクテットが "1" と "0" の 8 文字で出力されます。 各行の行頭には 16 進数の行番号が表示されます。 行末には ascii (または ebcdic) で表した場合の文字が表示されます。 -このモードでは \-r、\-p、\-i は機能しません。 +このモードでは \-p、\-i は機能しません。 .TP .IR \-e リトルエンディアンの 16 進ダンプに切り替える。 diff --git a/runtime/doc/xxd-pl.1 b/runtime/doc/xxd-pl.1 index d9fa9be203..877b7e45fc 100644 --- a/runtime/doc/xxd-pl.1 +++ b/runtime/doc/xxd-pl.1 @@ -66,7 +66,7 @@ Prze Opcja ta zapisuje oktety jako osiem cyfr 1 lub 0 zamiast normalnego zrzutu heksowego. Kada linia jest poprzedzona przez heksadecymalny numer linii a po nim jego reprezentacj w ascii (lub -ebcdic). Opcje linii polece \-r, \-p, \-i nie dziaaj w tym +ebcdic). Opcje linii polece \-p, \-i nie dziaaj w tym trybie. .TP .IR "\-c cols " | " \-cols cols" diff --git a/runtime/doc/xxd-pl.UTF-8.1 b/runtime/doc/xxd-pl.UTF-8.1 index e63ce64b74..a28dd63776 100644 --- a/runtime/doc/xxd-pl.UTF-8.1 +++ b/runtime/doc/xxd-pl.UTF-8.1 @@ -66,7 +66,7 @@ Przełącza do zrzutu bitowego (cyfr binarnych) zamiast heksowego. Opcja ta zapisuje oktety jako osiem cyfr 1 lub 0 zamiast normalnego zrzutu heksowego. Każda linia jest poprzedzona przez heksadecymalny numer linii a po nim jego reprezentacją w ascii (lub -ebcdic). Opcje linii poleceń \-r, \-p, \-i nie działają w tym +ebcdic). Opcje linii poleceń \-p, \-i nie działają w tym trybie. .TP .IR "\-c cols " | " \-cols cols" diff --git a/runtime/doc/xxd-ru.1 b/runtime/doc/xxd-ru.1 index 111946ab52..8f324327b7 100644 --- a/runtime/doc/xxd-ru.1 +++ b/runtime/doc/xxd-ru.1 @@ -70,7 +70,7 @@ xxd "1" "0". , - ( ascii ebcdic). \-r, \-p, \-i + ( ascii ebcdic). \-p, \-i . .TP .IR "\-c " | " \-cols " diff --git a/runtime/doc/xxd-ru.UTF-8.1 b/runtime/doc/xxd-ru.UTF-8.1 index 647845bab4..4a41d66db0 100644 --- a/runtime/doc/xxd-ru.UTF-8.1 +++ b/runtime/doc/xxd-ru.UTF-8.1 @@ -70,7 +70,7 @@ xxd позволяет выполнять декодирование в пото При использовании этого ключа вместо обычного шестнадцатеричного представления октетов используются наборы из восьми символов "1" и "0". Каждая строка предваряется номером строки в шестнадцатеричном виде, а завершается символьным -представлением (в виде ascii или ebcdic). Ключи \-r, \-p, \-i в этом режиме +представлением (в виде ascii или ebcdic). Ключи \-p, \-i в этом режиме не работают. .TP .IR "\-c кол " | " \-cols кол" diff --git a/runtime/doc/xxd.1 b/runtime/doc/xxd.1 index 7cd6ae8c77..f5a7c65893 100644 --- a/runtime/doc/xxd.1 +++ b/runtime/doc/xxd.1 @@ -64,7 +64,7 @@ Switch to bits (binary digits) dump, rather than hex dump. This option writes octets as eight digits "1"s and "0"s instead of a normal hexadecimal dump. Each line is preceded by a line number in hexadecimal and followed by an ASCII (or EBCDIC) representation. The command line switches -\-r, \-p, \-i do not work with this mode. +\-p, \-i do not work with this mode. .TP .IR "\-c cols " | " \-cols cols" Format @@ -133,7 +133,9 @@ it. Use the combination .I \-r \-p to read plain hexadecimal dumps without line number information and without a particular column layout. Additional whitespace and line breaks are allowed -anywhere. +anywhere. Use the combination +.I \-r \-b +to read a bits dump instead of a hex dump. .TP .IR \-R " " when In output the hex-value and the value are both colored with the same color diff --git a/runtime/doc/xxd.man b/runtime/doc/xxd.man index 06cc784c4a..56b69b4341 100644 --- a/runtime/doc/xxd.man +++ b/runtime/doc/xxd.man @@ -37,7 +37,7 @@ OPTIONS option writes octets as eight digits "1"s and "0"s instead of a normal hexadecimal dump. Each line is preceded by a line number in hexadecimal and followed by an ASCII (or EBCDIC) representa‐ - tion. The command line switches -r, -p, -i do not work with this + tion. The command line switches -p, -i do not work with this mode. -c cols | -cols cols @@ -97,7 +97,8 @@ OPTIONS truncating it. Use the combination -r -p to read plain hexadeci‐ mal dumps without line number information and without a particu‐ lar column layout. Additional whitespace and line breaks are al‐ - lowed anywhere. + lowed anywhere. Use the combination -r -b to read a bits dump + instead of a hex dump. -R when In output the hex-value and the value are both colored with the diff --git a/src/testdir/test_xxd.vim b/src/testdir/test_xxd.vim index 3c12899bf6..53e1a95c37 100644 --- a/src/testdir/test_xxd.vim +++ b/src/testdir/test_xxd.vim @@ -312,6 +312,31 @@ func Test_xxd_patch() call delete('Xxxdout') endfunc +func Test_xxd_patch_with_bitdump() + let cmd1 = 'silent !' .. s:xxd_cmd .. ' -r -b Xxxdin Xxxdfile' + let cmd2 = 'silent !' .. s:xxd_cmd .. ' -g1 Xxxdfile > Xxxdout' + + call writefile(["2: 01000001 01000001", "8: 01000010 01000010"], 'Xxxdin', 'D') + call writefile(['::::::::'], 'Xxxdfile', 'D') + exe cmd1 + exe cmd2 + call assert_equal(['00000000: 3a 3a 41 41 3a 3a 3a 3a 42 42 ::AA::::BB'], readfile('Xxxdout')) + + call writefile(["1: 01000011 01000011", "4: 01000100 01000100"], 'Xxxdin', 'D') + call writefile(['::::::::'], 'Xxxdfile', 'D') + exe cmd1 + exe cmd2 + call assert_equal(['00000000: 3a 43 43 3a 44 44 3a 3a 0a :CC:DD::.'], readfile('Xxxdout')) + + call writefile(["02: 01000101 01000101", "08: 01000110 01000110"], 'Xxxdin', 'D') + call writefile(['::::::::'], 'Xxxdfile', 'D') + exe cmd1 + exe cmd2 + call assert_equal(['00000000: 3a 3a 45 45 3a 3a 3a 3a 46 46 ::EE::::FF'], readfile('Xxxdout')) + + call delete('Xxxdout') +endfunc + " Various ways with wrong arguments that trigger the usage output. func Test_xxd_usage() for arg in ['-h', '-c', '-g', '-o', '-s', '-l', '-X', '-R', 'one two three'] @@ -336,6 +361,18 @@ func Test_xxd_bit_dump() bwipe! endfunc +func Test_xxd_revert_bit_dump() + new + exe 'r! printf "00000000: 01000001 01100010 01000011 01100100 01000101 01100110 01000111 01101000 AbCdEfGh" | ' . s:xxd_cmd . ' -r -b1 -c 8' + call assert_match('AbCdEfGh', join(getline(1, 3))) + bwipe! + + new + exe 'r! printf "00000000: 01000001 01100010 01000011 01100100 01000101 01100110 AbCdEf\n00000006: 01000111 01101000 Gh\n" | ' . s:xxd_cmd . ' -r -b1' + call assert_match('AbCdEfGh', join(getline(1, 3))) + bwipe! +endfunc + func Test_xxd_version() new exe 'r! ' . s:xxd_cmd . ' -v' diff --git a/src/version.c b/src/version.c index a946279ec2..5335352b03 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1998, /**/ 1997, /**/ diff --git a/src/xxd/xxd.c b/src/xxd/xxd.c index 1ba22477fd..56fb32183e 100644 --- a/src/xxd/xxd.c +++ b/src/xxd/xxd.c @@ -57,6 +57,7 @@ * 14.01.2022 Disable extra newlines with -c0 -p by Erik Auerswald. * 20.06.2022 Permit setting the variable names used by -i by David Gow * 31.08.2023 -R never/auto/always prints colored output + * 06.10.2023 enable -r -b to reverse bit dumps * * (c) 1990-1998 by Juergen Weigert (jnweiger@gmail.com) * @@ -135,7 +136,7 @@ extern void perror __P((char *)); # endif #endif -char version[] = "xxd 2023-09-04 by Juergen Weigert et al."; +char version[] = "xxd 2023-10-06 by Juergen Weigert et al."; #ifdef WIN32 char osver[] = " (Win32)"; #else @@ -234,7 +235,7 @@ exit_with_usage(void) fprintf(stderr, " or\n %s -r [-s [-]offset] [-c cols] [-ps] [infile [outfile]]\n", pname); fprintf(stderr, "Options:\n"); fprintf(stderr, " -a toggle autoskip: A single '*' replaces nul-lines. Default off.\n"); - fprintf(stderr, " -b binary digit dump (incompatible with -ps,-i,-r). Default hex.\n"); + fprintf(stderr, " -b binary digit dump (incompatible with -ps,-i). Default hex.\n"); fprintf(stderr, " -C capitalize variable names in C include file style (-i).\n"); fprintf(stderr, " -c cols format octets per line. Default 16 (-i: 12, -ps: 30).\n"); fprintf(stderr, " -E show characters in EBCDIC. Default ASCII.\n"); @@ -324,6 +325,17 @@ parse_hex_digit(int c) : -1; } +/* + * If "c" is a bin digit, return the value. + * Otherwise return -1. + */ + static int +parse_bin_digit(int c) +{ + return (c >= '0' && c <= '1') ? c - '0' + : -1; +} + /* * Ignore text on "fpi" until end-of-line or end-of-file. * Return the '\n' or EOF character. @@ -352,7 +364,7 @@ huntype( int hextype, long base_off) { - int c, ign_garb = 1, n1 = -1, n2 = 0, n3, p = cols; + int c, ign_garb = 1, n1 = -1, n2 = 0, n3, p = cols, bt, b = 0, bcnt = 0; long have_off = 0, want_off = 0; rewind(fpi); @@ -368,25 +380,60 @@ huntype( if (hextype == HEX_POSTSCRIPT && (c == ' ' || c == '\n' || c == '\t')) continue; - n3 = n2; - n2 = n1; + if (hextype == HEX_NORMAL || hextype == HEX_POSTSCRIPT) + { + n3 = n2; + n2 = n1; - n1 = parse_hex_digit(c); - if (n1 == -1 && ign_garb) - continue; + n1 = parse_hex_digit(c); + if (n1 == -1 && ign_garb) + continue; + } + else /* HEX_BITS */ + { + n1 = parse_hex_digit(c); + if (n1 == -1 && ign_garb) + continue; + + bt = parse_bin_digit(c); + if (bt != -1) + { + b = ((b << 1) | bt); + ++bcnt; + } + } ign_garb = 0; - if (!hextype && (p >= cols)) + if ((hextype != HEX_POSTSCRIPT) && (p >= cols)) { - if (n1 < 0) - { - p = 0; - continue; - } - want_off = (want_off << 4) | n1; - continue; - } + if (hextype == HEX_NORMAL) + { + if (n1 < 0) + { + p = 0; + continue; + } + want_off = (want_off << 4) | n1; + } + else /* HEX_BITS */ + { + n1 = parse_hex_digit(c); + if (n1 >= 0) + { + want_off = (want_off << 4) | n1; + } + + if (bt < 0) + { + p = 0; + bcnt = 0; + b = 0; + continue; + } + } + continue; + } if (base_off + want_off != have_off) { @@ -402,23 +449,40 @@ huntype( putc_or_die(0, fpo); } - if (n2 >= 0 && n1 >= 0) - { - putc_or_die((n2 << 4) | n1, fpo); - have_off++; - want_off++; - n1 = -1; - if (!hextype && (++p >= cols)) - /* skip the rest of the line as garbage */ - c = skip_to_eol(fpi, c); - } - else if (n1 < 0 && n2 < 0 && n3 < 0) - /* already stumbled into garbage, skip line, wait and see */ - c = skip_to_eol(fpi, c); + if (hextype == HEX_NORMAL || hextype == HEX_POSTSCRIPT) + { + if (n2 >= 0 && n1 >= 0) + { + putc_or_die((n2 << 4) | n1, fpo); + have_off++; + want_off++; + n1 = -1; + if (!hextype && (++p >= cols)) + /* skip the rest of the line as garbage */ + c = skip_to_eol(fpi, c); + } + else if (n1 < 0 && n2 < 0 && n3 < 0) + /* already stumbled into garbage, skip line, wait and see */ + c = skip_to_eol(fpi, c); + } + else /* HEX_BITS */ + { + if (bcnt == 8) + { + putc_or_die(b, fpo); + have_off++; + want_off++; + b = 0; + bcnt = 0; + if (++p >= cols) + /* skip the rest of the line as garbage */ + c = skip_to_eol(fpi, c); + } + } if (c == '\n') { - if (!hextype) + if (hextype == HEX_NORMAL || hextype == HEX_BITS) want_off = 0; p = cols; ign_garb = 1; @@ -847,12 +911,17 @@ main(int argc, char *argv[]) } if (revert) - { - if (hextype && (hextype != HEX_POSTSCRIPT)) - error_exit(-1, "Sorry, cannot revert this type of hexdump"); - return huntype(fp, fpo, cols, hextype, - negseek ? -seekoff : seekoff); - } + switch (hextype) + { + case HEX_NORMAL: + case HEX_POSTSCRIPT: + case HEX_BITS: + return huntype(fp, fpo, cols, hextype, + negseek ? -seekoff : seekoff); + break; + default: + error_exit(-1, "Sorry, cannot revert this type of hexdump"); + } if (seekoff || negseek || !relseek) { From e6c9aa5e6a88d539a412a9b5526f41ea101aa185 Mon Sep 17 00:00:00 2001 From: Ernie Rael Date: Fri, 6 Oct 2023 19:55:52 +0200 Subject: [PATCH 07/47] patch 9.0.1999: Vim9: some error messages can be improved Problem: Vim9: some error messages can be improved Solution: Mention the defining class for variable access error message closes: #13272 Signed-off-by: Christian Brabandt Signed-off-by: Yegappan Lakshmanan Co-authored-by: Ernie Rael --- src/eval.c | 18 ++--- src/proto/vim9class.pro | 1 + src/testdir/test_vim9_class.vim | 113 ++++++++++++++++++++++++++++++++ src/version.c | 2 + src/vim9class.c | 75 +++++++++++++++++++-- src/vim9compile.c | 2 +- src/vim9execute.c | 4 +- src/vim9expr.c | 8 +-- 8 files changed, 204 insertions(+), 19 deletions(-) diff --git a/src/eval.c b/src/eval.c index a9f7112f2d..d9fbec234c 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1105,26 +1105,28 @@ get_lval_check_access( #endif if (cl_exec == NULL || cl_exec != cl) { + char *msg = NULL; switch (om->ocm_access) { case VIM_ACCESS_PRIVATE: - semsg(_(e_cannot_access_private_variable_str), - om->ocm_name, cl->class_name); - return FAIL; + msg = e_cannot_access_private_variable_str; + break; case VIM_ACCESS_READ: // If [idx] or .key following, read only OK. if (*p == '[' || *p == '.') break; if ((flags & GLV_READ_ONLY) == 0) - { - semsg(_(e_variable_is_not_writable_str), - om->ocm_name, cl->class_name); - return FAIL; - } + msg = e_variable_is_not_writable_str; break; case VIM_ACCESS_ALL: break; } + if (msg != NULL) + { + emsg_var_cl_define(msg, om->ocm_name, 0, cl); + return FAIL; + } + } return OK; } diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro index f2e642ec70..e36c7e288e 100644 --- a/src/proto/vim9class.pro +++ b/src/proto/vim9class.pro @@ -16,6 +16,7 @@ ocmember_T *object_member_lookup(class_T *cl, char_u *name, size_t namelen, int int object_method_idx(class_T *cl, char_u *name, size_t namelen); ufunc_T *object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx); ocmember_T *member_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx); +void emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl); ufunc_T *method_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx); int inside_class(cctx_T *cctx_arg, class_T *cl); void copy_object(typval_T *from, typval_T *to); diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index dc539aca49..0c8fd7057a 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -1725,6 +1725,119 @@ def Test_class_member() v9.CheckSourceFailure(lines, 'E1326: Variable not found on object "A": bar', 5) enddef +" These messages should show the defining class of the variable (base class), +" not the class that did the reference (super class) +def Test_defining_class_message() + var lines =<< trim END + vim9script + + class Base + this._v1: list> + endclass + + class Child extends Base + endclass + + var o = Child.new() + var x = o._v1 + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_v1" in class "Base"', 11) + lines =<< trim END + vim9script + + class Base + this._v1: list> + endclass + + class Child extends Base + endclass + + def F() + var o = Child.new() + var x = o._v1 + enddef + F() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_v1" in class "Base"', 2) + lines =<< trim END + vim9script + + class Base + this.v1: list> + endclass + + class Child extends Base + endclass + + var o = Child.new() + o.v1 = [] + END + v9.CheckSourceFailure(lines, 'E1335: Variable "v1" in class "Base" is not writable', 11) + lines =<< trim END + vim9script + + class Base + this.v1: list> + endclass + + class Child extends Base + endclass + + def F() + var o = Child.new() + o.v1 = [] + enddef + F() + END + + # Attempt to read a private variable that is in the middle + # of the class hierarchy. + v9.CheckSourceFailure(lines, 'E1335: Variable "v1" in class "Base" is not writable', 2) + lines =<< trim END + vim9script + + class Base0 + endclass + + class Base extends Base0 + this._v1: list> + endclass + + class Child extends Base + endclass + + def F() + var o = Child.new() + var x = o._v1 + enddef + F() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_v1" in class "Base"', 2) + + # Attempt to read a private variable that is at the start + # of the class hierarchy. + lines =<< trim END + vim9script + + class Base0 + endclass + + class Base extends Base0 + endclass + + class Child extends Base + this._v1: list> + endclass + + def F() + var o = Child.new() + var x = o._v1 + enddef + F() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_v1" in class "Child"', 2) +enddef + func Test_class_garbagecollect() let lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c index 5335352b03..8dea204b2b 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1999, /**/ 1998, /**/ diff --git a/src/vim9class.c b/src/vim9class.c index 295716e18e..7d1a7fdcaa 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -2167,8 +2167,8 @@ get_member_tv( if (*name == '_') { - semsg(_(e_cannot_access_private_variable_str), m->ocm_name, - cl->class_name); + emsg_var_cl_define(e_cannot_access_private_variable_str, + m->ocm_name, 0, cl); return FAIL; } @@ -2329,8 +2329,8 @@ class_object_index( if (*name == '_') { - semsg(_(e_cannot_access_private_variable_str), m->ocm_name, - cl->class_name); + emsg_var_cl_define(e_cannot_access_private_variable_str, + m->ocm_name, 0, cl); return FAIL; } @@ -2580,6 +2580,57 @@ member_lookup( return object_member_lookup(cl, name, namelen, idx); } +/* + * Find the class that defines the named member. Look up the hierarchy + * starting at "cl". + * + * Return the class that defines the member "name", else NULL. + * Fill in "p_m", if specified, for ocmember_T in found class. + */ +// NOTE: if useful for something could also indirectly return vartype and idx. + static class_T * +class_defining_member(class_T *cl, char_u *name, size_t len, ocmember_T **p_m) +{ + class_T *cl_found = NULL; + vartype_T vartype = VAR_UNKNOWN; + ocmember_T *m_found = NULL; + + len = len != 0 ? len : STRLEN(name); + + // Loop assumes if member is not defined in "cl", then it is not + // defined in any super class; the last class where it's found is the + // class where it is defined. Once the vartype is found, the other + // type is no longer checked. + for (class_T *super = cl; super != NULL; super = super->class_extends) + { + class_T *cl_tmp = NULL; + ocmember_T *m = NULL; + if (vartype == VAR_UNKNOWN || vartype == VAR_OBJECT) + { + if ((m = object_member_lookup(super, name, len, NULL)) != NULL) + { + cl_tmp = super; + vartype = VAR_OBJECT; + } + } + if (vartype == VAR_UNKNOWN || vartype == VAR_CLASS) + { + if (( m = class_member_lookup(super, name, len, NULL)) != NULL) + { + cl_tmp = super; + vartype = VAR_OBJECT; + } + } + if (cl_tmp == NULL) + break; // member is not in this or any super class. + cl_found = cl_tmp; + m_found = m; + } + if (p_m != NULL) + *p_m = m_found; + return cl_found; +} + /* * Lookup a class or object method by name. If v_type is VAR_CLASS, then * lookup a class method and if it is VAR_OBJECT, then lookup a object method. @@ -2853,6 +2904,22 @@ object_free_nonref(int copyID) return did_free; } +/* + * Output message which takes a variable name and the class that defines it. + * "cl" is that class where the name was found. Search "cl"'s hierarchy to + * find the defining class. + */ + void +emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl) +{ + ocmember_T *m; + class_T *cl_def = class_defining_member(cl, name, len, &m); + if (cl_def != NULL) + semsg(_(msg), m->ocm_name, cl_def->class_name); + else + emsg(_(e_internal_error_please_report_a_bug)); +} + /* * Echo a class or object method not found message. */ diff --git a/src/vim9compile.c b/src/vim9compile.c index 136bea49d3..828fe02e81 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1616,7 +1616,7 @@ lhs_class_member_modifiable(lhs_T *lhs, char_u *var_start, cctx_T *cctx) char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE) ? e_cannot_access_private_variable_str : e_variable_is_not_writable_str; - semsg(_(msg), m->ocm_name, cl->class_name); + emsg_var_cl_define(msg, m->ocm_name, 0, cl); return FALSE; } diff --git a/src/vim9execute.c b/src/vim9execute.c index a899745207..f237132a89 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2180,8 +2180,8 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx) { if (*member == '_') { - semsg(_(e_cannot_access_private_variable_str), - m->ocm_name, cl->class_name); + emsg_var_cl_define(e_cannot_access_private_variable_str, + m->ocm_name, 0, cl); status = FAIL; } diff --git a/src/vim9expr.c b/src/vim9expr.c index 05eb524180..c15021e82a 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -407,8 +407,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) { if (*name == '_' && !inside_class(cctx, cl)) { - semsg(_(e_cannot_access_private_variable_str), m->ocm_name, - cl->class_name); + emsg_var_cl_define(e_cannot_access_private_variable_str, + m->ocm_name, 0, cl); return FAIL; } @@ -443,8 +443,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) // it is defined. if (*name == '_' && cctx->ctx_ufunc->uf_class != cl) { - semsg(_(e_cannot_access_private_variable_str), m->ocm_name, - cl->class_name); + emsg_var_cl_define(e_cannot_access_private_variable_str, + m->ocm_name, 0, cl); return FAIL; } From 2a281ccca017fb5e8ffd20a86aa390431224a2fd Mon Sep 17 00:00:00 2001 From: dkearns Date: Sat, 7 Oct 2023 04:59:42 +1100 Subject: [PATCH 08/47] runtime(sh): Update ftplugin (#13213) Rename 'keywordprg' user command to ShKeywordPrg as this is just a leaking implementation detail. Signed-off-by: Christian Brabandt --- runtime/ftplugin/sh.vim | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/runtime/ftplugin/sh.vim b/runtime/ftplugin/sh.vim index c227838d18..c1a6bc5ade 100644 --- a/runtime/ftplugin/sh.vim +++ b/runtime/ftplugin/sh.vim @@ -3,7 +3,8 @@ " Maintainer: Doug Kearns " Previous Maintainer: Dan Sharp " Contributor: Enno Nagel -" Last Change: 2023 Aug 29 +" Eisuke Kawashima +" Last Change: 2023 Sep 28 if exists("b:did_ftplugin") finish @@ -39,16 +40,16 @@ if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") let b:undo_ftplugin ..= " | unlet! b:browsefilter" endif -if (exists("b:is_bash") && (b:is_bash == 1)) +if get(b:, "is_bash", 0) if !has("gui_running") && executable("less") - command! -buffer -nargs=1 Help silent exe '!bash -c "{ help "" 2>/dev/null || man ""; } | LESS= less"' | redraw! - elseif has('terminal') - command! -buffer -nargs=1 Help silent exe ':term bash -c "help "" 2>/dev/null || man """' + command! -buffer -nargs=1 ShKeywordPrg silent exe '!bash -c "{ help "" 2>/dev/null || man ""; } | LESS= less"' | redraw! + elseif has("terminal") + command! -buffer -nargs=1 ShKeywordPrg silent exe ':term bash -c "help "" 2>/dev/null || man """' else - command! -buffer -nargs=1 Help echo system('bash -c "help " 2>/dev/null || man ""') + command! -buffer -nargs=1 ShKeywordPrg echo system('bash -c "help " 2>/dev/null || man ""') endif - setlocal keywordprg=:Help - let b:undo_ftplugin ..= " | setl kp< | sil! delc -buffer Help" + setlocal keywordprg=:ShKeywordPrg + let b:undo_ftplugin ..= " | setl kp< | sil! delc -buffer ShKeywordPrg" endif let &cpo = s:save_cpo From 1087b8c29ab521106c5b6cc85d5b38244f0d9c1d Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sat, 7 Oct 2023 22:03:18 +0200 Subject: [PATCH 09/47] patch 9.0.2000: Vim9: use-after-free in deep call stack Problem: Vim9: use-after-free in deep call stack Solution: Get the objct pointer from execution stack closes: #13296 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan --- src/testdir/test_vim9_class.vim | 40 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9execute.c | 6 +++++ 3 files changed, 48 insertions(+) diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 0c8fd7057a..0e70c9f6fe 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -6989,4 +6989,44 @@ func Test_object_variable_complex_type_check() call v9.CheckSourceSuccess(lines) endfunc +" Test for recursively calling an object method. This used to cause an +" use-after-free error. +def Test_recursive_object_method_call() + var lines =<< trim END + vim9script + class A + this.val: number = 0 + def Foo(): number + if this.val >= 90 + return this.val + endif + this.val += 1 + return this.Foo() + enddef + endclass + var a = A.new() + assert_equal(90, a.Foo()) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for recursively calling a class method. +def Test_recursive_class_method_call() + var lines =<< trim END + vim9script + class A + static val: number = 0 + static def Foo(): number + if val >= 90 + return val + endif + val += 1 + return Foo() + enddef + endclass + assert_equal(90, A.Foo()) + END + v9.CheckSourceSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 8dea204b2b..5d1c1c9441 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2000, /**/ 1999, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index f237132a89..826282241b 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -559,6 +559,12 @@ call_dfunc( arg_to_add + STACK_FRAME_SIZE + varcount)) return FAIL; + // The object pointer is in the execution typval stack. The GA_GROW call + // above may have reallocated the execution typval stack. So the object + // pointer may not be valid anymore. Get the object pointer again from the + // execution stack. + obj = STACK_TV_BOT(0) - argcount - vararg_count - 1; + // If depth of calling is getting too high, don't execute the function. if (funcdepth_increment() == FAIL) return FAIL; From 9771b2a67f825bdc6e5c615141d22c665952dc86 Mon Sep 17 00:00:00 2001 From: Ernie Rael Date: Sat, 7 Oct 2023 22:05:40 +0200 Subject: [PATCH 10/47] patch 9.0.2001: Vim9: segfault with islocked() Problem: Vim9: segfault with islocked() Solution: Check that the lval pointer is not null for objects and class variables closes: #13295 Signed-off-by: Christian Brabandt Co-authored-by: Ernie Rael --- src/eval.c | 6 +-- src/evalfunc.c | 16 +++++++ src/structs.h | 17 ++++--- src/testdir/test_vim9_class.vim | 80 +++++++++++++++++++++++++++++++++ src/version.c | 2 + 5 files changed, 112 insertions(+), 9 deletions(-) diff --git a/src/eval.c b/src/eval.c index d9fbec234c..93109effb9 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1375,6 +1375,7 @@ get_lval( && v_type != VAR_OBJECT && v_type != VAR_CLASS) { + // TODO: have a message with obj/class, not just dict, if (!quiet) semsg(_(e_dot_can_only_be_used_on_dictionary_str), name); return NULL; @@ -1385,6 +1386,7 @@ get_lval( && v_type != VAR_OBJECT && v_type != VAR_CLASS) { + // TODO: have a message with obj/class, not just dict/list/blob, if (!quiet) emsg(_(e_can_only_index_list_dictionary_or_blob)); return NULL; @@ -1739,10 +1741,6 @@ get_lval( } } - // TODO: dont' check access if inside class - // TODO: is GLV_READ_ONLY the right thing to use - // for class/object member access? - // Probably in some cases. Need inside class check if (lp->ll_valtype == NULL) { int m_idx; diff --git a/src/evalfunc.c b/src/evalfunc.c index 501ee03582..b840220bd0 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -7347,6 +7347,22 @@ f_islocked(typval_T *argvars, typval_T *rettv) || tv_islocked(&di->di_tv)); } } + else if (lv.ll_object != NULL) + { + typval_T *tv = ((typval_T *)(lv.ll_object + 1)) + lv.ll_oi; + rettv->vval.v_number = tv_islocked(tv); +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: f_islocked(): name %s (obj)", lv.ll_name); +#endif + } + else if (lv.ll_class != NULL) + { + typval_T *tv = &lv.ll_class->class_members_tv[lv.ll_oi]; + rettv->vval.v_number = tv_islocked(tv); +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: f_islocked(): name %s (cl)", lv.ll_name); +#endif + } else if (lv.ll_range) emsg(_(e_range_not_allowed)); else if (lv.ll_newkey != NULL) diff --git a/src/structs.h b/src/structs.h index 680ed6dbd8..ee688848e7 100644 --- a/src/structs.h +++ b/src/structs.h @@ -4547,11 +4547,18 @@ typedef struct * "tv" points to the (first) list item value * "li" points to the (first) list item * "range", "n1", "n2" and "empty2" indicate what items are used. - * For a member in a class/object: TODO: verify fields + * For a plain class or object: + * "name" points to the variable name. + * "exp_name" is NULL. + * "tv" points to the variable + * "is_root" TRUE + * For a variable in a class/object: (class is not NULL) * "name" points to the (expanded) variable name. * "exp_name" NULL or non-NULL, to be freed later. - * "tv" points to the (first) list item value - * "oi" index into member array, see _type to determine which array + * "tv" May point to class/object variable. + * "object" object containing variable, NULL if class variable + * "class" class of object or class containing variable + * "oi" index into class/object of tv * For an existing Dict item: * "name" points to the (expanded) variable name. * "exp_name" NULL or non-NULL, to be freed later. @@ -4591,8 +4598,8 @@ typedef struct lval_S object_T *ll_object; // The object or NULL, class is not NULL class_T *ll_class; // The class or NULL, object may be NULL int ll_oi; // The object/class member index - int ll_is_root; // Special case. ll_tv is lval_root, - // ignore the rest. + int ll_is_root; // TRUE if ll_tv is the lval_root, like a + // plain object/class. ll_tv is variable. } lval_T; /** diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 0e70c9f6fe..555c46cc8e 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -4161,6 +4161,86 @@ def Test_lockvar_general() v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_v1" in class "C"') enddef +" Test builtin islocked() +def Test_lockvar_islocked() + # Can't lock class/object variable + # Lock class/object variable's value + # Lock item of variabl's value (a list item) + # varible is at index 1 within class/object + var lines =<< trim END + vim9script + + class C + this.o0: list> = [ [0], [1], [2]] + this.o1: list> = [[10], [11], [12]] + static c0: list> = [[20], [21], [22]] + static c1: list> = [[30], [31], [32]] + endclass + + def LockIt(arg: any) + lockvar arg + enddef + + def UnlockIt(arg: any) + unlockvar arg + enddef + + var obj = C.new() + #lockvar obj.o1 # can't lock something you can't write to + + try + lockvar obj.o1 # can't lock something you can't write to + call assert_false(1, '"lockvar obj.o1" should have failed') + catch + call assert_exception('E1335:') + endtry + + LockIt(obj.o1) # but can lock it's value + assert_equal(1, islocked("obj.o1")) + assert_equal(1, islocked("obj.o1[0]")) + assert_equal(1, islocked("obj.o1[1]")) + UnlockIt(obj.o1) + assert_equal(0, islocked("obj.o1")) + assert_equal(0, islocked("obj.o1[0]")) + + lockvar obj.o1[0] + assert_equal(0, islocked("obj.o1")) + assert_equal(1, islocked("obj.o1[0]")) + assert_equal(0, islocked("obj.o1[1]")) + unlockvar obj.o1[0] + assert_equal(0, islocked("obj.o1")) + assert_equal(0, islocked("obj.o1[0]")) + + # Same thing, but with a static + + try + lockvar C.c1 # can't lock something you can't write to + call assert_false(1, '"lockvar C.c1" should have failed') + catch + call assert_exception('E1335:') + endtry + + LockIt(C.c1) # but can lock it's value + assert_equal(1, islocked("C.c1")) + assert_equal(1, islocked("C.c1[0]")) + assert_equal(1, islocked("C.c1[1]")) + UnlockIt(C.c1) + assert_equal(0, islocked("C.c1")) + assert_equal(0, islocked("C.c1[0]")) + + lockvar C.c1[0] + assert_equal(0, islocked("C.c1")) + assert_equal(1, islocked("C.c1[0]")) + assert_equal(0, islocked("C.c1[1]")) + unlockvar C.c1[0] + assert_equal(0, islocked("C.c1")) + assert_equal(0, islocked("C.c1[0]")) + END + v9.CheckSourceSuccess(lines) + lines =<< trim END + END +enddef + " Test for a private object method def Test_private_object_method() # Try calling a private method using an object (at the script level) diff --git a/src/version.c b/src/version.c index 5d1c1c9441..e408dc1f71 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2001, /**/ 2000, /**/ From 75b277d35ce207ceb5e3645a962cfd59657d1d73 Mon Sep 17 00:00:00 2001 From: Antonio Giovanni Colombo Date: Sun, 8 Oct 2023 19:04:21 +0200 Subject: [PATCH 11/47] translation(it): updated translation of xxd manpage Signed-off-by: Christian Brabandt --- runtime/doc/xxd-it.1 | 6 ++++-- runtime/doc/xxd-it.UTF-8.1 | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/doc/xxd-it.1 b/runtime/doc/xxd-it.1 index ed4fd81946..9311ed585c 100644 --- a/runtime/doc/xxd-it.1 +++ b/runtime/doc/xxd-it.1 @@ -131,9 +131,11 @@ Ricostruisce: converte (o mette una patch) da immagine esadecimale, a file binar Se non scrive sullo `standard output', xxd scrive nel file di output in maniera continua, senza interruzioni. Usare la combinazione .I \-r \-p -per leggere dump in stile esadecimale semplice, senza l'informazione del numero +per leggere un dump in stile esadecimale semplice, senza l'informazione del numero di riga e senza un particolare tracciato di colonna. Spazi o righe vuote -possono essere presenti [e vengono ignorati]. +possono essere presenti dappertutto [e vengono ignorati]. Usare la combinazione +.I \-r \-b +per leggere un dump binario, invece che un dump esadecimale. .TP .IR \-R " "[quando] Nell'output i valori esadecimali e i caratteri corrispondenti hanno entrambi diff --git a/runtime/doc/xxd-it.UTF-8.1 b/runtime/doc/xxd-it.UTF-8.1 index 8653de972f..66df3ffe66 100644 --- a/runtime/doc/xxd-it.UTF-8.1 +++ b/runtime/doc/xxd-it.UTF-8.1 @@ -131,9 +131,11 @@ Ricostruisce: converte (o mette una patch) da immagine esadecimale, a file binar Se non scrive sullo `standard output', xxd scrive nel file di output in maniera continua, senza interruzioni. Usare la combinazione .I \-r \-p -per leggere dump in stile esadecimale semplice, senza l'informazione del numero +per leggere un dump in stile esadecimale semplice, senza l'informazione del numero di riga e senza un particolare tracciato di colonna. Spazi o righe vuote -possono essere presenti [e vengono ignorati]. +possono essere presenti dappertutto [e vengono ignorati]. Usare la combinazione +.I \-r \-b +per leggere un dump binario, invece che un dump esadecimale. .TP .IR \-R " "[quando] Nell'output i valori esadecimali e i caratteri corrispondenti hanno entrambi From b852305dbf42f1206ecc6ae414fc200235fe2963 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sun, 8 Oct 2023 19:07:39 +0200 Subject: [PATCH 12/47] patch 9.0.2002: Vim9: need cleanup of class related interface code Problem: Vim9: need cleanup of class related interface code Solution: Remove the unused class variable and class method related code for interfaces. Remove unused class variable and class method related code for interfaces. Refactor the code. Optimize the object/class member double lookup in compile_lhs(). Change unused global functions to static functions. closes: #13302 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan --- src/proto/vim9class.pro | 4 - src/testdir/test_vim9_class.vim | 57 ++++- src/version.c | 2 + src/vim9class.c | 428 ++++++++++++++------------------ src/vim9cmds.c | 2 +- src/vim9compile.c | 29 ++- 6 files changed, 266 insertions(+), 256 deletions(-) diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro index e36c7e288e..c9e19d0067 100644 --- a/src/proto/vim9class.pro +++ b/src/proto/vim9class.pro @@ -10,11 +10,8 @@ ufunc_T *find_class_func(char_u **arg); int class_member_idx(class_T *cl, char_u *name, size_t namelen); ocmember_T *class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx); int class_method_idx(class_T *cl, char_u *name, size_t namelen); -ufunc_T *class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx); -int object_member_idx(class_T *cl, char_u *name, size_t namelen); ocmember_T *object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx); int object_method_idx(class_T *cl, char_u *name, size_t namelen); -ufunc_T *object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx); ocmember_T *member_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx); void emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl); ufunc_T *method_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx); @@ -26,7 +23,6 @@ void class_unref(class_T *cl); int class_free_nonref(int copyID); int set_ref_in_classes(int copyID); void object_created(object_T *obj); -void object_cleared(object_T *obj); int object_free_nonref(int copyID); void method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len); void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len); diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 555c46cc8e..c911206c9f 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -975,6 +975,28 @@ def Test_class_new_with_object_member() Check() END v9.CheckSourceSuccess(lines) + + # Try using "this." argument in a class method + lines =<< trim END + vim9script + class A + this.val = 10 + static def Foo(this.val: number) + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1390: Cannot use an object variable "this.val" except with the "new" method', 4) + + # Try using "this." argument in an object method + lines =<< trim END + vim9script + class A + this.val = 10 + def Foo(this.val: number) + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1390: Cannot use an object variable "this.val" except with the "new" method', 4) enddef def Test_class_object_member_inits() @@ -1722,7 +1744,7 @@ def Test_class_member() var a = A.new() var v = a.bar END - v9.CheckSourceFailure(lines, 'E1326: Variable not found on object "A": bar', 5) + v9.CheckSourceFailure(lines, 'E1337: Class variable "bar" not found in class "A"', 5) enddef " These messages should show the defining class of the variable (base class), @@ -4255,7 +4277,7 @@ def Test_private_object_method() var a = A.new() a._Foo() END - v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()', 9) + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 9) # Try calling a private method using an object (from a def function) lines =<< trim END @@ -4468,7 +4490,7 @@ def Test_private_object_method() var c = C.new() assert_equal(1234, c._Foo()) END - v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()', 16) + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 16) # Using "_" prefix in a method name should fail outside of a class lines =<< trim END @@ -4494,7 +4516,7 @@ def Test_private_class_method() endclass A._Foo() END - v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()', 8) + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 8) # Try calling a class private method (from a def function) lines =<< trim END @@ -5122,7 +5144,7 @@ def Test_class_variable_access_using_object() var a = A.new() echo a.svar2 END - v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8) + v9.CheckSourceFailure(lines, 'E1337: Class variable "svar2" not found in class "A"', 8) # Cannot write to a class variable using an object in script context lines =<< trim END @@ -5597,7 +5619,7 @@ def Test_class_variable() var a = A.new() var i = a.val END - v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7) + v9.CheckSourceFailure(lines, 'E1337: Class variable "val" not found in class "A"', 7) # Modifying a class variable using an object at function level lines =<< trim END @@ -5969,6 +5991,18 @@ def Test_extend_interface() END v9.CheckSourceSuccess(lines) + # extending empty interface + lines =<< trim END + vim9script + interface A + endinterface + interface B extends A + endinterface + class C implements B + endclass + END + v9.CheckSourceSuccess(lines) + lines =<< trim END vim9script interface A @@ -6567,6 +6601,17 @@ def Test_reserved_varname() o.F() END v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3) + + # class variable name + if kword != 'this' + lines =<< trim eval END + vim9script + class C + public static {kword}: list = [1, 2, 3] + endclass + END + v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3) + endif endfor enddef diff --git a/src/version.c b/src/version.c index e408dc1f71..6a92ab75c7 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2002, /**/ 2001, /**/ diff --git a/src/vim9class.c b/src/vim9class.c index 7d1a7fdcaa..0cb353bb57 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -555,7 +555,6 @@ validate_abstract_class_methods( intf_variable_present( char_u *intf_class_name, ocmember_T *if_var, - int is_class_var, ocmember_T *cl_mt, int cl_member_count, class_T *extends_cl) @@ -600,15 +599,10 @@ intf_variable_present( if (!variable_present && extends_cl != NULL) { - int ext_cl_count = is_class_var - ? extends_cl->class_class_member_count - : extends_cl->class_obj_member_count; - ocmember_T *ext_cl_mt = is_class_var - ? extends_cl->class_class_members - : extends_cl->class_obj_members; + int ext_cl_count = extends_cl->class_obj_member_count; + ocmember_T *ext_cl_mt = extends_cl->class_obj_members; return intf_variable_present(intf_class_name, if_var, - is_class_var, ext_cl_mt, - ext_cl_count, + ext_cl_mt, ext_cl_count, extends_cl->class_extends); } @@ -616,43 +610,32 @@ intf_variable_present( } /* - * Check the variables of the interface class "ifcl" match the class variables - * ("classmembers_gap") and object variables ("objmembers_gap") of a class. - * Returns TRUE if the class and object variables names are valid. + * Check the variables of the interface class "ifcl" match object variables + * ("objmembers_gap") of a class. + * Returns TRUE if the object variables names are valid. */ static int validate_interface_variables( char_u *intf_class_name, class_T *ifcl, - garray_T *classmembers_gap, garray_T *objmembers_gap, class_T *extends_cl) { - for (int loop = 1; loop <= 2; ++loop) + int if_count = ifcl->class_obj_member_count; + if (if_count == 0) + return TRUE; + + ocmember_T *if_ms = ifcl->class_obj_members; + ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data); + int cl_count = objmembers_gap->ga_len; + for (int if_i = 0; if_i < if_count; ++if_i) { - // loop == 1: check class variables - // loop == 2: check object variables - int is_class_var = (loop == 1); - int if_count = is_class_var ? ifcl->class_class_member_count - : ifcl->class_obj_member_count; - if (if_count == 0) - continue; - ocmember_T *if_ms = is_class_var ? ifcl->class_class_members - : ifcl->class_obj_members; - ocmember_T *cl_ms = (ocmember_T *)(is_class_var - ? classmembers_gap->ga_data - : objmembers_gap->ga_data); - int cl_count = is_class_var ? classmembers_gap->ga_len - : objmembers_gap->ga_len; - for (int if_i = 0; if_i < if_count; ++if_i) - { - if (!intf_variable_present(intf_class_name, &if_ms[if_i], - is_class_var, cl_ms, cl_count, extends_cl)) - { - semsg(_(e_variable_str_of_interface_str_not_implemented), - if_ms[if_i].ocm_name, intf_class_name); - return FALSE; - } + if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms, + cl_count, extends_cl)) + { + semsg(_(e_variable_str_of_interface_str_not_implemented), + if_ms[if_i].ocm_name, intf_class_name); + return FALSE; } } @@ -685,7 +668,6 @@ intf_method_type_matches(ufunc_T *if_method, ufunc_T *cl_method) static int intf_method_present( ufunc_T *if_ufunc, - int is_class_method, ufunc_T **cl_fp, int cl_count, class_T *extends_cl) @@ -707,15 +689,10 @@ intf_method_present( if (!method_present && extends_cl != NULL) { - ufunc_T **ext_cl_fp = (ufunc_T **)(is_class_method - ? extends_cl->class_class_functions - : extends_cl->class_obj_methods); - int ext_cl_count = is_class_method - ? extends_cl->class_class_function_count - : extends_cl->class_obj_method_count; - return intf_method_present(if_ufunc, is_class_method, ext_cl_fp, - ext_cl_count, - extends_cl->class_extends); + ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods); + int ext_cl_count = extends_cl->class_obj_method_count; + return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count, + extends_cl->class_extends); } return method_present; @@ -733,37 +710,25 @@ intf_method_present( validate_interface_methods( char_u *intf_class_name, class_T *ifcl, - garray_T *classfunctions_gap, garray_T *objmethods_gap, class_T *extends_cl) { - for (int loop = 1; loop <= 2; ++loop) + int if_count = ifcl->class_obj_method_count; + if (if_count == 0) + return TRUE; + + ufunc_T **if_fp = ifcl->class_obj_methods; + ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data); + int cl_count = objmethods_gap->ga_len; + for (int if_i = 0; if_i < if_count; ++if_i) { - // loop == 1: check class methods - // loop == 2: check object methods - int is_class_method = (loop == 1); - int if_count = is_class_method ? ifcl->class_class_function_count - : ifcl->class_obj_method_count; - if (if_count == 0) - continue; - ufunc_T **if_fp = is_class_method ? ifcl->class_class_functions - : ifcl->class_obj_methods; - ufunc_T **cl_fp = (ufunc_T **)(is_class_method - ? classfunctions_gap->ga_data - : objmethods_gap->ga_data); - int cl_count = is_class_method ? classfunctions_gap->ga_len - : objmethods_gap->ga_len; - for (int if_i = 0; if_i < if_count; ++if_i) - { - char_u *if_name = if_fp[if_i]->uf_name; + char_u *if_name = if_fp[if_i]->uf_name; - if (!intf_method_present(if_fp[if_i], is_class_method, cl_fp, - cl_count, extends_cl)) - { - semsg(_(e_method_str_of_interface_str_not_implemented), - if_name, intf_class_name); - return FALSE; - } + if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl)) + { + semsg(_(e_method_str_of_interface_str_not_implemented), + if_name, intf_class_name); + return FALSE; } } @@ -781,8 +746,6 @@ validate_interface_methods( validate_implements_classes( garray_T *impl_gap, class_T **intf_classes, - garray_T *classfunctions_gap, - garray_T *classmembers_gap, garray_T *objmethods_gap, garray_T *objmembers_gap, class_T *extends_cl) @@ -816,15 +779,14 @@ validate_implements_classes( ++ifcl->class_refcount; // check the variables of the interface match the members of the class - success = validate_interface_variables(impl, ifcl, classmembers_gap, - objmembers_gap, extends_cl); + success = validate_interface_variables(impl, ifcl, objmembers_gap, + extends_cl); // check the functions/methods of the interface match the // functions/methods of the class if (success) - success = validate_interface_methods(impl, ifcl, - classfunctions_gap, objmethods_gap, - extends_cl); + success = validate_interface_methods(impl, ifcl, objmethods_gap, + extends_cl); clear_tv(&tv); } @@ -1873,9 +1835,7 @@ ex_class(exarg_T *eap) intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len); success = validate_implements_classes(&ga_impl, intf_classes, - &classfunctions, &classmembers, - &objmethods, &objmembers, - extends_cl); + &objmethods, &objmembers, extends_cl); } // Check no function argument name is used as a class member. @@ -2172,18 +2132,90 @@ get_member_tv( return FAIL; } - // The object only contains a pointer to the class, the member - // values array follows right after that. - object_T *obj = rettv->vval.v_object; if (is_object) { + // The object only contains a pointer to the class, the member values + // array follows right after that. + object_T *obj = rettv->vval.v_object; typval_T *tv = (typval_T *)(obj + 1) + m_idx; copy_tv(tv, rettv); + object_unref(obj); } else + { copy_tv(&cl->class_members_tv[m_idx], rettv); + class_unref(cl); + } + + return OK; +} - object_unref(obj); +/* + * Call an object or class method "name" in class "cl". The method return + * value is returned in "rettv". + */ + static int +call_oc_method( + class_T *cl, + char_u *name, + size_t len, + char_u *name_end, + evalarg_T *evalarg, + char_u **arg, + typval_T *rettv) +{ + ufunc_T *fp; + typval_T argvars[MAX_FUNC_ARGS + 1]; + int argcount = 0; + + fp = method_lookup(cl, rettv->v_type, name, len, NULL); + if (fp == NULL) + { + method_not_found_msg(cl, rettv->v_type, name, len); + return FAIL; + } + + if (*fp->uf_name == '_') + { + // Cannot access a private method outside of a class + semsg(_(e_cannot_access_private_method_str), fp->uf_name); + return FAIL; + } + + char_u *argp = name_end; + int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount); + if (ret == FAIL) + return FAIL; + + funcexe_T funcexe; + CLEAR_FIELD(funcexe); + funcexe.fe_evaluate = TRUE; + if (rettv->v_type == VAR_OBJECT) + { + funcexe.fe_object = rettv->vval.v_object; + ++funcexe.fe_object->obj_refcount; + } + + // Clear the class or object after calling the function, in + // case the refcount is one. + typval_T tv_tofree = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Call the user function. Result goes into rettv; + int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe, + NULL); + + // Clear the previous rettv and the arguments. + clear_tv(&tv_tofree); + for (int idx = 0; idx < argcount; ++idx) + clear_tv(&argvars[idx]); + + if (error != FCERR_NONE) + { + user_func_error(error, printable_func_name(fp), funcexe.fe_found_var); + return FAIL; + } + *arg = argp; return OK; } @@ -2242,104 +2274,22 @@ class_object_index( } if (*name_end == '(') - { - ufunc_T *fp; - - fp = method_lookup(cl, rettv->v_type, name, len, NULL); - if (fp == NULL) - { - method_not_found_msg(cl, rettv->v_type, name, len); - return FAIL; - } - - typval_T argvars[MAX_FUNC_ARGS + 1]; - int argcount = 0; - - if (*fp->uf_name == '_') - { - // Cannot access a private method outside of a class - semsg(_(e_cannot_access_private_method_str), name); - return FAIL; - } - - char_u *argp = name_end; - int ret = get_func_arguments(&argp, evalarg, 0, - argvars, &argcount); - if (ret == FAIL) - return FAIL; - - funcexe_T funcexe; - CLEAR_FIELD(funcexe); - funcexe.fe_evaluate = TRUE; - if (rettv->v_type == VAR_OBJECT) - { - funcexe.fe_object = rettv->vval.v_object; - ++funcexe.fe_object->obj_refcount; - } - - // Clear the class or object after calling the function, in - // case the refcount is one. - typval_T tv_tofree = *rettv; - rettv->v_type = VAR_UNKNOWN; - - // Call the user function. Result goes into rettv; - int error = call_user_func_check(fp, argcount, argvars, - rettv, &funcexe, NULL); - - // Clear the previous rettv and the arguments. - clear_tv(&tv_tofree); - for (int idx = 0; idx < argcount; ++idx) - clear_tv(&argvars[idx]); - - if (error != FCERR_NONE) - { - user_func_error(error, printable_func_name(fp), - funcexe.fe_found_var); - return FAIL; - } - *arg = argp; - return OK; - } + // Invoke the class or object method + return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv); - else if (rettv->v_type == VAR_OBJECT) + else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS) { // Search in the object member variable table and the class member // variable table. - if (get_member_tv(cl, TRUE, name, len, rettv) == OK) + int is_object = rettv->v_type == VAR_OBJECT; + if (get_member_tv(cl, is_object, name, len, rettv) == OK) { *arg = name_end; return OK; } if (did_emsg == did_emsg_save) - member_not_found_msg(cl, VAR_OBJECT, name, len); - } - - else if (rettv->v_type == VAR_CLASS) - { - int m_idx; - - // class member - ocmember_T *m = class_member_lookup(cl, name, len, &m_idx); - if (m == NULL) - { - member_not_found_msg(cl, VAR_CLASS, name, len); - return FAIL; - } - - if (*name == '_') - { - emsg_var_cl_define(e_cannot_access_private_variable_str, - m->ocm_name, 0, cl); - return FAIL; - } - - typval_T *tv = &cl->class_members_tv[m_idx]; - copy_tv(tv, rettv); - class_unref(cl); - - *arg = name_end; - return OK; + member_not_found_msg(cl, is_object, name, len); } return FAIL; @@ -2432,24 +2382,12 @@ class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx) return ret_m; } -/* - * Returns the index of class method "name" in the class "cl". - * Returns -1, if the method is not found. - */ - int -class_method_idx(class_T *cl, char_u *name, size_t namelen) -{ - int idx; - class_method_lookup(cl, name, namelen, &idx); - return idx; -} - /* * Returns a pointer to the class method "name" in class "cl". * Returns NULL if the method is not found. * The method index is set in "idx". */ - ufunc_T * + static ufunc_T * class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx) { ufunc_T *ret_fp = NULL; @@ -2470,12 +2408,24 @@ class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx) return ret_fp; } +/* + * Returns the index of class method "name" in the class "cl". + * Returns -1, if the method is not found. + */ + int +class_method_idx(class_T *cl, char_u *name, size_t namelen) +{ + int idx; + class_method_lookup(cl, name, namelen, &idx); + return idx; +} + /* * Returns the index of object member variable "name" in the class "cl". * Returns -1, if the variable is not found. * If "namelen" is zero, then it is assumed that "name" is NUL terminated. */ - int + static int object_member_idx(class_T *cl, char_u *name, size_t namelen) { int idx; @@ -2518,24 +2468,12 @@ object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx) return ret_m; } -/* - * Returns the index of object method "name" in the class "cl". - * Returns -1, if the method is not found. - */ - int -object_method_idx(class_T *cl, char_u *name, size_t namelen) -{ - int idx; - object_method_lookup(cl, name, namelen, &idx); - return idx; -} - /* * Returns a pointer to the object method "name" in class "cl". * Returns NULL if the method is not found. * The object method index is set in "idx". */ - ufunc_T * + static ufunc_T * object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx) { ufunc_T *ret_fp = NULL; @@ -2558,6 +2496,18 @@ object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx) return ret_fp; } +/* + * Returns the index of object method "name" in the class "cl". + * Returns -1, if the method is not found. + */ + int +object_method_idx(class_T *cl, char_u *name, size_t namelen) +{ + int idx; + object_method_lookup(cl, name, namelen, &idx); + return idx; +} + /* * Lookup a class or object member variable by name. If v_type is VAR_CLASS, * then lookup a class member variable and if it is VAR_OBJECT, then lookup a @@ -2681,42 +2631,6 @@ copy_object(typval_T *from, typval_T *to) } } -/* - * Free an object. - */ - static void -object_clear(object_T *obj) -{ - // Avoid a recursive call, it can happen if "obj" has a circular reference. - obj->obj_refcount = INT_MAX; - - class_T *cl = obj->obj_class; - - if (!cl) - return; - - // the member values are just after the object structure - typval_T *tv = (typval_T *)(obj + 1); - for (int i = 0; i < cl->class_obj_member_count; ++i) - clear_tv(tv + i); - - // Remove from the list headed by "first_object". - object_cleared(obj); - - vim_free(obj); - class_unref(cl); -} - -/* - * Unreference an object. - */ - void -object_unref(object_T *obj) -{ - if (obj != NULL && --obj->obj_refcount <= 0) - object_clear(obj); -} - /* * Make a copy of a class. */ @@ -2866,7 +2780,7 @@ static object_T *next_nonref_obj = NULL; * Call this function when an object has been cleared and is about to be freed. * It is removed from the list headed by "first_object". */ - void + static void object_cleared(object_T *obj) { if (obj->obj_next_used != NULL) @@ -2881,6 +2795,42 @@ object_cleared(object_T *obj) next_nonref_obj = obj->obj_next_used; } +/* + * Free an object. + */ + static void +object_clear(object_T *obj) +{ + // Avoid a recursive call, it can happen if "obj" has a circular reference. + obj->obj_refcount = INT_MAX; + + class_T *cl = obj->obj_class; + + if (!cl) + return; + + // the member values are just after the object structure + typval_T *tv = (typval_T *)(obj + 1); + for (int i = 0; i < cl->class_obj_member_count; ++i) + clear_tv(tv + i); + + // Remove from the list headed by "first_object". + object_cleared(obj); + + vim_free(obj); + class_unref(cl); +} + +/* + * Unreference an object. + */ + void +object_unref(object_T *obj) +{ + if (obj != NULL && --obj->obj_refcount <= 0) + object_clear(obj); +} + /* * Go through the list of all objects and free items without "copyID". */ diff --git a/src/vim9cmds.c b/src/vim9cmds.c index 0be207795f..8b5b569808 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -254,7 +254,7 @@ compile_lock_unlock( { // Push the class of the bare class variable name name = cl->class_name; - len = STRLEN(name); + len = (int)STRLEN(name); #ifdef LOG_LOCKVAR ch_log(NULL, "LKVAR: ... cctx_class_member: name %s", name); diff --git a/src/vim9compile.c b/src/vim9compile.c index 828fe02e81..7e1914b694 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2011,16 +2011,33 @@ compile_lhs( // for an object or class member get the type of the member class_T *cl = lhs->lhs_type->tt_class; int is_object = lhs->lhs_type->tt_type == VAR_OBJECT; + char_u *name = var_start + lhs->lhs_varlen + 1; + size_t namelen = lhs->lhs_end - var_start - lhs->lhs_varlen - 1; - if (!lhs_class_member_modifiable(lhs, var_start, cctx)) + ocmember_T *m = member_lookup(cl, lhs->lhs_type->tt_type, + name, namelen, &lhs->lhs_member_idx); + if (m == NULL) + { + member_not_found_msg(cl, lhs->lhs_type->tt_type, name, namelen); return FAIL; + } - lhs->lhs_member_type = class_member_type(cl, - is_object, - after + 1, lhs->lhs_end, - &lhs->lhs_member_idx); - if (lhs->lhs_member_idx < 0) + // If it is private member variable, then accessing it outside the + // class is not allowed. + // If it is a read only class variable, then it can be modified + // only inside the class where it is defined. + if ((m->ocm_access != VIM_ACCESS_ALL) && + ((is_object && !inside_class(cctx, cl)) + || (!is_object && cctx->ctx_ufunc->uf_class != cl))) + { + char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE) + ? e_cannot_access_private_variable_str + : e_variable_is_not_writable_str; + emsg_var_cl_define(msg, m->ocm_name, 0, cl); return FAIL; + } + + lhs->lhs_member_type = m->ocm_type; } else { From 1e33cd72b60a119a038952bb658862d038602f76 Mon Sep 17 00:00:00 2001 From: Enno Date: Sun, 8 Oct 2023 19:14:07 +0200 Subject: [PATCH 13/47] runtime: make command name for &iskeywordprg more unique (#13297) See https://github.com/vim/vim/pull/13213/commits by @dkearns: Rename 'keywordprg' user command to ShKeywordPrg as this is just a leaking implementation detail. Signed-off-by: Christian Brabandt --- runtime/ftplugin/gpg.vim | 12 ++++++------ runtime/ftplugin/modconf.vim | 12 ++++++------ runtime/ftplugin/muttrc.vim | 12 ++++++------ runtime/ftplugin/readline.vim | 10 +++++----- runtime/ftplugin/sshconfig.vim | 12 ++++++------ runtime/ftplugin/sudoers.vim | 12 ++++++------ runtime/ftplugin/systemd.vim | 11 ++++++----- runtime/ftplugin/udevrules.vim | 12 ++++++------ runtime/ftplugin/zsh.vim | 12 ++++++------ 9 files changed, 53 insertions(+), 52 deletions(-) diff --git a/runtime/ftplugin/gpg.vim b/runtime/ftplugin/gpg.vim index 2415555e09..7fb4f47ed8 100644 --- a/runtime/ftplugin/gpg.vim +++ b/runtime/ftplugin/gpg.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: gpg(1) configuration file " Previous Maintainer: Nikolai Weibull -" Latest Revision: 2008-07-09 +" Latest Revision: 2023-10-07 if exists("b:did_ftplugin") finish @@ -17,17 +17,17 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql if has('unix') && executable('less') if !has('gui_running') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 GpgKeywordPrg \ silent exe '!' . 'LESS= MANPAGER="less --pattern=''^\s+--' . . '\b'' --hilite-search" man ' . 'gpg' | \ redraw! elseif has('terminal') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 GpgKeywordPrg \ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+--' . . '\b', '\') . ''' --hilite-search" man ' . 'gpg' endif - if exists(':Sman') == 2 + if exists(':GpgKeywordPrg') == 2 setlocal iskeyword+=- - setlocal keywordprg=:Sman - let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer Sman' + setlocal keywordprg=:GpgKeywordPrg + let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer GpgKeywordPrg' endif endif diff --git a/runtime/ftplugin/modconf.vim b/runtime/ftplugin/modconf.vim index d5eda5af21..22d18a9aad 100644 --- a/runtime/ftplugin/modconf.vim +++ b/runtime/ftplugin/modconf.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: modules.conf(5) configuration file " Previous Maintainer: Nikolai Weibull -" Latest Revision: 2008-07-09 +" Latest Revision: 2023-10-07 if exists("b:did_ftplugin") finish @@ -18,17 +18,17 @@ setlocal formatoptions-=t formatoptions+=croql if has('unix') && executable('less') if !has('gui_running') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 ModconfKeywordPrg \ silent exe '!' . 'LESS= MANPAGER="less --pattern=''^\s{,8}' . . '\b'' --hilite-search" man ' . 'modprobe.d' | \ redraw! elseif has('terminal') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 ModconfKeywordPrg \ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s{,8}' . . '\b', '\') . ''' --hilite-search" man ' . 'modprobe.d' endif - if exists(':Sman') == 2 + if exists(':ModconfKeywordPrg') == 2 setlocal iskeyword+=- - setlocal keywordprg=:Sman - let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer Sman' + setlocal keywordprg=:ModconfKeywordPrg + let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer ModconfKeywordPrg' endif endif diff --git a/runtime/ftplugin/muttrc.vim b/runtime/ftplugin/muttrc.vim index 7a4eb7a8bb..c9f6df31d0 100644 --- a/runtime/ftplugin/muttrc.vim +++ b/runtime/ftplugin/muttrc.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: mutt RC File " Previous Maintainer: Nikolai Weibull -" Latest Revision: 2006-04-19 +" Latest Revision: 2023-10-07 if exists("b:did_ftplugin") finish @@ -20,17 +20,17 @@ let &l:include = '^\s*source\>' if has('unix') && executable('less') if !has('gui_running') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 MuttrcKeywordPrg \ silent exe '!' . 'LESS= MANPAGER="less --pattern=''^\s+' . . '\b'' --hilite-search" man ' . 'muttrc' | \ redraw! elseif has('terminal') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 MuttrcKeywordPrg \ silent exe 'term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . . '\b', '\') . ''' --hilite-search" man ' . 'muttrc' endif - if exists(':Sman') == 2 + if exists(':MuttrcKeywordPrg') == 2 setlocal iskeyword+=- - setlocal keywordprg=:Sman - let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer Sman' + setlocal keywordprg=:MuttrcKeywordPrg + let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer MuttrcKeywordPrg' endif endif diff --git a/runtime/ftplugin/readline.vim b/runtime/ftplugin/readline.vim index a696da2701..181d8ac661 100644 --- a/runtime/ftplugin/readline.vim +++ b/runtime/ftplugin/readline.vim @@ -32,17 +32,17 @@ endif if has('unix') && executable('less') if !has('gui_running') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 ReadlineKeywordPrg \ silent exe '!' . 'LESS= MANPAGER="less --pattern=''^\s+' . . '\b'' --hilite-search" man ' . '3 readline' | \ redraw! elseif has('terminal') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 ReadlineKeywordPrg \ silent exe 'term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . . '\b', '\') . ''' --hilite-search" man ' . '3 readline' endif - if exists(':Sman') == 2 + if exists(':ReadlineKeywordPrg') == 2 setlocal iskeyword+=- - setlocal keywordprg=:Sman - let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer Sman' + setlocal keywordprg=:ReadlineKeywordPrg + let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer ReadlineKeywordPrg' endif endif diff --git a/runtime/ftplugin/sshconfig.vim b/runtime/ftplugin/sshconfig.vim index c9a5cfaa68..4a054da52f 100644 --- a/runtime/ftplugin/sshconfig.vim +++ b/runtime/ftplugin/sshconfig.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: OpenSSH client configuration file " Previous Maintainer: Nikolai Weibull -" Latest Revision: 2008-07-09 +" Latest Revision: 2023-10-07 if exists("b:did_ftplugin") finish @@ -16,17 +16,17 @@ let b:undo_ftplugin = 'setlocal com< cms< fo<' if has('unix') && executable('less') if !has('gui_running') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 SshconfigKeywordPrg \ silent exe '!' . 'LESS= MANPAGER="less --pattern=''^\s+' . . '$'' --hilite-search" man ' . 'ssh_config' | \ redraw! elseif has('terminal') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 SshconfigKeywordPrg \ silent exe 'term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s+' . . '$', '\') . ''' --hilite-search" man ' . 'ssh_config' endif - if exists(':Sman') == 2 + if exists(':SshconfigKeywordPrg') == 2 setlocal iskeyword+=- - setlocal keywordprg=:Sman - let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer Sman' + setlocal keywordprg=:SshconfigKeywordPrg + let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SshconfigKeywordPrg' endif endif diff --git a/runtime/ftplugin/sudoers.vim b/runtime/ftplugin/sudoers.vim index b4123620af..81ce7906a9 100644 --- a/runtime/ftplugin/sudoers.vim +++ b/runtime/ftplugin/sudoers.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: sudoers(5) configuration files " Previous Maintainer: Nikolai Weibull -" Latest Revision: 2008-07-09 +" Latest Revision: 2023-10-07 if exists("b:did_ftplugin") finish @@ -17,17 +17,17 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql if has('unix') && executable('less') if !has('gui_running') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 SudoersKeywordPrg \ silent exe '!' . 'LESS= MANPAGER="less --pattern=''\b' . . '\b'' --hilite-search" man ' . 'sudoers' | \ redraw! elseif has('terminal') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 SudoersKeywordPrg \ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('\b' . . '\b', '\') . ''' --hilite-search" man ' . 'sudoers' endif - if exists(':Sman') == 2 + if exists(':SudoersKeywordPrg') == 2 setlocal iskeyword+=- - setlocal keywordprg=:Sman - let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer Sman' + setlocal keywordprg=:SudoersKeywordPrg + let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SudoersKeywordPrg' endif endif diff --git a/runtime/ftplugin/systemd.vim b/runtime/ftplugin/systemd.vim index e60a5e4960..8bcacdd381 100644 --- a/runtime/ftplugin/systemd.vim +++ b/runtime/ftplugin/systemd.vim @@ -1,6 +1,7 @@ " Vim filetype plugin file " Language: systemd.unit(5) " Keyword Lookup Support: Enno Nagel +" Latest Revision: 2023-10-07 if !exists('b:did_ftplugin') " Looks a lot like dosini files. @@ -9,11 +10,11 @@ endif if has('unix') && executable('less') if !has('gui_running') - command -buffer -nargs=1 Sman silent exe '!' . KeywordLookup_systemd() | redraw! + command -buffer -nargs=1 SystemdKeywordPrg silent exe '!' . KeywordLookup_systemd() | redraw! elseif has('terminal') - command -buffer -nargs=1 Sman silent exe 'term ' . KeywordLookup_systemd() + command -buffer -nargs=1 SystemdKeywordPrg silent exe 'term ' . KeywordLookup_systemd() endif - if exists(':Sman') == 2 + if exists(':SystemdKeywordPrg') == 2 if !exists('*KeywordLookup_systemd') function KeywordLookup_systemd(keyword) abort let matches = matchlist(getline(search('\v^\s*\[\s*.+\s*\]\s*$', 'nbWz')), '\v^\s*\[\s*(\k+).*\]\s*$') @@ -26,11 +27,11 @@ if has('unix') && executable('less') endfunction endif setlocal iskeyword+=- - setlocal keywordprg=:Sman + setlocal keywordprg=:SystemdKeywordPrg if !exists('b:undo_ftplugin') || empty(b:undo_ftplugin) let b:undo_ftplugin = 'setlocal keywordprg< iskeyword<' else - let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer Sman' + let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer SystemdKeywordPrg' endif endif endif diff --git a/runtime/ftplugin/udevrules.vim b/runtime/ftplugin/udevrules.vim index 83fb728a54..ec365f04c2 100644 --- a/runtime/ftplugin/udevrules.vim +++ b/runtime/ftplugin/udevrules.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: udev(8) rules file " Previous Maintainer: Nikolai Weibull -" Latest Revision: 2008-07-09 +" Latest Revision: 2023-10-07 if exists("b:did_ftplugin") finish @@ -17,17 +17,17 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql if has('unix') && executable('less') if !has('gui_running') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 UdevrulesKeywordPrg \ silent exe '!' . 'LESS= MANPAGER="less --pattern=''^\s{,8}' . . '\b'' --hilite-search" man ' . 'udev' | \ redraw! elseif has('terminal') - command -buffer -nargs=1 Sman + command -buffer -nargs=1 UdevrulesKeywordPrg \ silent exe ':term ' . 'env LESS= MANPAGER="less --pattern=''' . escape('^\s{,8}' . . '\b', '\') . ''' --hilite-search" man ' . 'udev' endif - if exists(':Sman') == 2 + if exists(':UdevrulesKeywordPrg') == 2 setlocal iskeyword+=- - setlocal keywordprg=:Sman - let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer Sman' + setlocal keywordprg=:UdevrulesKeywordPrg + let b:undo_ftplugin .= '| setlocal keywordprg< iskeyword< | sil! delc -buffer UdevrulesKeywordPrg' endif endif diff --git a/runtime/ftplugin/zsh.vim b/runtime/ftplugin/zsh.vim index ed75d04003..40986fccbe 100644 --- a/runtime/ftplugin/zsh.vim +++ b/runtime/ftplugin/zsh.vim @@ -2,7 +2,7 @@ " Language: Zsh shell script " Maintainer: Christian Brabandt " Previous Maintainer: Nikolai Weibull -" Latest Revision: 2021-04-03 +" Latest Revision: 2023-10-07 " License: Vim (see :h license) " Repository: https://github.com/chrisbra/vim-zsh @@ -20,17 +20,17 @@ let b:undo_ftplugin = "setl com< cms< fo< " if executable('zsh') && &shell !~# '/\%(nologin\|false\)$' if !has('gui_running') && executable('less') - command! -buffer -nargs=1 RunHelp silent exe '!MANPAGER= zsh -c "autoload -Uz run-help; run-help 2>/dev/null | LESS= less"' | redraw! + command! -buffer -nargs=1 ZshKeywordPrg silent exe '!MANPAGER= zsh -c "autoload -Uz run-help; run-help 2>/dev/null | LESS= less"' | redraw! elseif has('terminal') - command! -buffer -nargs=1 RunHelp silent exe ':term zsh -c "autoload -Uz run-help; run-help "' + command! -buffer -nargs=1 ZshKeywordPrg silent exe ':term zsh -c "autoload -Uz run-help; run-help "' else - command! -buffer -nargs=1 RunHelp echo system('zsh -c "autoload -Uz run-help; run-help 2>/dev/null"') + command! -buffer -nargs=1 ZshKeywordPrg echo system('zsh -c "autoload -Uz run-help; run-help 2>/dev/null"') endif if !exists('current_compiler') compiler zsh endif - setlocal keywordprg=:RunHelp - let b:undo_ftplugin .= 'keywordprg< | sil! delc -buffer RunHelp' + setlocal keywordprg=:ZshKeywordPrg + let b:undo_ftplugin .= 'keywordprg< | sil! delc -buffer ZshKeywordPrg' endif let b:match_words = '\:\:\:\' From 7879bc5c13311c1fb6497776ed7804400852460a Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 8 Oct 2023 20:36:44 +0200 Subject: [PATCH 14/47] patch 9.0.2003: xxd: compilation warning Problem: xxd: compilation warning Solution: initialize variables Signed-off-by: Christian Brabandt --- src/version.c | 2 ++ src/xxd/xxd.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/version.c b/src/version.c index 6a92ab75c7..9a653f0741 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2003, /**/ 2002, /**/ diff --git a/src/xxd/xxd.c b/src/xxd/xxd.c index 56fb32183e..544aa50b95 100644 --- a/src/xxd/xxd.c +++ b/src/xxd/xxd.c @@ -136,7 +136,7 @@ extern void perror __P((char *)); # endif #endif -char version[] = "xxd 2023-10-06 by Juergen Weigert et al."; +char version[] = "xxd 2023-10-08 by Juergen Weigert et al."; #ifdef WIN32 char osver[] = " (Win32)"; #else @@ -364,7 +364,7 @@ huntype( int hextype, long base_off) { - int c, ign_garb = 1, n1 = -1, n2 = 0, n3, p = cols, bt, b = 0, bcnt = 0; + int c, ign_garb = 1, n1 = -1, n2 = 0, n3 = 0, p = cols, bt = 0, b = 0, bcnt = 0; long have_off = 0, want_off = 0; rewind(fpi); From d4afbdd0715c722cfc73d3a8ab9e578667615faa Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 9 Oct 2023 08:15:00 +0200 Subject: [PATCH 15/47] patch 9.0.2004: Missing test file Problem: Missing test file Solution: git-add the file to the repo closes: #13305 Signed-off-by: Christian Brabandt --- src/testdir/crash/vim_msg_trunc_poc | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 src/testdir/crash/vim_msg_trunc_poc diff --git a/src/testdir/crash/vim_msg_trunc_poc b/src/testdir/crash/vim_msg_trunc_poc new file mode 100644 index 0000000000..73b04bec35 --- /dev/null +++ b/src/testdir/crash/vim_msg_trunc_poc @@ -0,0 +1 @@ +lv\ngggggi;norm:᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌󠁲᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌᠌ diff --git a/src/version.c b/src/version.c index 9a653f0741..6602a489e1 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2004, /**/ 2003, /**/ From b56cef0be08febc7e8edb8a87592f0a347c2793f Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 9 Oct 2023 17:52:14 +0200 Subject: [PATCH 16/47] patch 9.0.2005: partially revert patch v9.0.1997 Problem: partially revert patch v9.0.1997 Solution: add a comment, to make clear it's not used related: #13288 Signed-off-by: Christian Brabandt --- src/move.c | 10 ++++++++++ src/version.c | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/move.c b/src/move.c index a0d3a6014a..42878e4ad9 100644 --- a/src/move.c +++ b/src/move.c @@ -1753,7 +1753,12 @@ scrolldown( ++row; } if (col > width2 && width2 > 0) + { row += col / width2; + // even so col is not used anymore, + // make sure it is correct, just in case + col = col % width2; + } if (row >= curwin->w_height) { curwin->w_curswant = curwin->w_virtcol @@ -1986,7 +1991,12 @@ adjust_skipcol(void) ++row; } if (col > width2) + { row += col / width2; + // col may no longer be used, but make + // sure it is correct anyhow, just in case + col = col % width2; + } if (row >= curwin->w_height) { if (curwin->w_skipcol == 0) diff --git a/src/version.c b/src/version.c index 6602a489e1..e862e33324 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2005, /**/ 2004, /**/ From f4ee1cb74ba86383190ffcda84147892f7740f21 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Mon, 9 Oct 2023 17:57:27 +0200 Subject: [PATCH 17/47] patch 9.0.2006: Vim9: need more tests Problem: Vim9: need more tests Solution: add additional disassembly tests closes: #13305 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan --- src/testdir/test_vim9_disassemble.vim | 179 +++++++++++++++++++++++++- src/version.c | 2 + 2 files changed, 178 insertions(+), 3 deletions(-) diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 206908b79c..6e27dbdd00 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -488,6 +488,8 @@ if has('job') var Pp = null_partial var jj = null_job var cc = null_channel + var oo = null_object + var nc = null_class enddef def Test_disassemble_assign_null() @@ -525,6 +527,14 @@ if has('job') '\d\+ PUSHCHANNEL 0\_s*' .. '\d\+ STORE $\d\_s*' .. + 'var oo = null_object\_s*' .. + '\d\+ PUSHOBJ null\_s*' .. + '\d\+ STORE $\d\_s*' .. + + 'var nc = null_class\_s*' .. + '\d\+ PUSHCLASS null\_s*' .. + '\d\+ STORE $\d\_s*' .. + '\d\+ RETURN void', res) enddef @@ -2968,7 +2978,7 @@ def BitShift() var a = 1 << 2 var b = 8 >> 1 var c = a << b - var d = b << a + var d = b >> a enddef def Test_disassemble_bitshift() @@ -2983,10 +2993,10 @@ def Test_disassemble_bitshift() '3 LOAD $1\_s*' .. '4 OPNR <<\_s*' .. '5 STORE $2\_s*' .. - 'var d = b << a\_s*' .. + 'var d = b >> a\_s*' .. '6 LOAD $1\_s*' .. '7 LOAD $0\_s*' .. - '8 OPNR <<\_s*' .. + '8 OPNR >>\_s*' .. '9 STORE $3\_s*' .. '10 RETURN void', instr) enddef @@ -3108,4 +3118,167 @@ def Test_disassemble_interface_static_member() unlet g:instr2 enddef +" Disassemble instructions for loading and storing class variables +def Test_disassemble_class_variable() + var lines =<< trim END + vim9script + + class A + public static val = 10 + def Foo(): number + val = 20 + return val + enddef + endclass + + g:instr = execute('disassemble A.Foo') + END + v9.CheckScriptSuccess(lines) + assert_match('Foo\_s*' .. + 'val = 20\_s*' .. + '0 PUSHNR 20\_s*' .. + '1 STORE CLASSMEMBER A.val\_s*' .. + 'return val\_s*' .. + '2 LOAD CLASSMEMBER A.val\_s*' .. + '3 RETURN', g:instr) + + unlet g:instr +enddef + +" Disassemble instructions for METHODCALL +def Test_disassemble_methodcall() + var lines =<< trim END + vim9script + interface A + def Foo() + endinterface + def Bar(a: A) + a.Foo() + enddef + g:instr = execute('disassemble Bar') + END + v9.CheckScriptSuccess(lines) + assert_match('\d*_Bar\_s*' .. + 'a.Foo()\_s*' .. + '0 LOAD arg\[-1\]\_s*' .. + '1 METHODCALL A.Foo(argc 0)\_s*' .. + '2 DROP\_s*' .. + '3 RETURN void', g:instr) + + unlet g:instr +enddef + +" Disassemble instructions for ISN_JUMP_IF_ARG_NOT_SET +def Test_disassemble_ifargnotset() + var lines =<< trim END + vim9script + class A + this.val: number = 10 + endclass + g:instr = execute('disassemble A.new') + END + v9.CheckScriptSuccess(lines) + assert_match('new\_s*' .. + '0 NEW A size \d\+\_s*' .. + '1 PUSHNR 10\_s*' .. + '2 STORE_THIS 0\_s*' .. + 'ifargisset 0 this.val = val\_s*' .. + '3 JUMP_IF_ARG_NOT_SET arg\[-1\] -> 8\_s*' .. + '4 LOAD arg\[-1\]\_s*' .. + '5 PUSHNR 0\_s*' .. + '6 LOAD $0\_s*' .. + '7 STOREINDEX object\_s*' .. + '8 RETURN object', g:instr) + + unlet g:instr +enddef + +" Disassemble instructions for ISN_COMPARECLASS and ISN_COMPAREOBJECT +def Test_disassemble_compare_class_object() + var lines =<< trim END + vim9script + class A + endclass + class B + endclass + def Foo(a: A, b: B) + if A == B + endif + if a == b + endif + enddef + g:instr = execute('disassemble Foo') + END + v9.CheckScriptSuccess(lines) + assert_match('\d*_Foo\_s*' .. + 'if A == B\_s*' .. + '0 LOADSCRIPT A-0 from .*\_s*' .. + '1 LOADSCRIPT B-1 from .*\_s*' .. + '2 COMPARECLASS ==\_s*' .. + '3 JUMP_IF_FALSE -> 4\_s*' .. + 'endif\_s*' .. + 'if a == b\_s*' .. + '4 LOAD arg\[-2\]\_s*' .. + '5 LOAD arg\[-1\]\_s*' .. + '6 COMPAREOBJECT ==\_s*' .. + '7 JUMP_IF_FALSE -> 8\_s*' .. + 'endif\_s*' .. + '8 RETURN void', g:instr) + unlet g:instr +enddef + +" Disassemble instructions for ISN_CHECKTYPE with a float|number +def Test_checktype_float() + var lines =<< trim END + vim9script + def Foo() + var f: float = 0.0 + var a: any + f += a + enddef + g:instr = execute('disassemble Foo') + END + v9.CheckScriptSuccess(lines) + assert_match('\d*_Foo\_s*' .. + 'var f: float = 0.0\_s*' .. + '0 PUSHF 0.0\_s*' .. + '1 STORE $0\_s*' .. + 'var a: any\_s*' .. + 'f += a\_s*' .. + '2 LOAD $0\_s*' .. + '3 LOAD $1\_s*' .. + '4 CHECKTYPE float|number stack\[-1\]\_s*' .. + '5 OPANY +\_s*' .. + '6 STORE $0\_s*' .. + '7 RETURN void', g:instr) + unlet g:instr +enddef + +" Disassemble instructions for ISN_FUNCREF with a class +def Test_funcref_with_class() + var lines =<< trim END + vim9script + class A + def Foo() + enddef + endclass + class B extends A + def Foo() + enddef + endclass + def Bar(a: A) + defer a.Foo() + enddef + g:instr = execute('disassemble Bar') + END + v9.CheckScriptSuccess(lines) + assert_match('\d*_Bar\_s*' .. + 'defer a.Foo()\_s*' .. + '0 LOAD arg\[-1\]\_s*' .. + '1 FUNCREF A.Foo\_s*' .. + '2 DEFEROBJ 0 args\_s*' .. + '3 RETURN void', g:instr) + unlet g:instr +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index e862e33324..08c34e1894 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2006, /**/ 2005, /**/ From e4671890220ef3f2bca43fde6ffe5d3ef3ed0e42 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Mon, 9 Oct 2023 18:01:06 +0200 Subject: [PATCH 18/47] patch 9.0.2007: Vim9: covariant parameter types allowed Problem: Vim9: covariant parameter types allowed when assigning functions Solution: Enforce invariant type check for arguments and return value when assigning a funcref closes: #13299 closes: #13305 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan --- src/testdir/test_vim9_class.vim | 37 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9type.c | 6 ++---- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index c911206c9f..ce7d5f7faa 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -7154,4 +7154,41 @@ def Test_recursive_class_method_call() v9.CheckSourceSuccess(lines) enddef +" Test for checking the argument types and the return type when assigning a +" funcref to make sure the invariant class type is used. +def Test_funcref_argtype_returntype_check() + var lines =<< trim END + vim9script + class A + endclass + class B extends A + endclass + + def Foo(p: B): B + return B.new() + enddef + + var Bar: func(A): A = Foo + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object): object but got func(object): object', 11) + + lines =<< trim END + vim9script + class A + endclass + class B extends A + endclass + + def Foo(p: B): B + return B.new() + enddef + + def Baz() + var Bar: func(A): A = Foo + enddef + Baz() + END + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object): object but got func(object): object', 1) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 08c34e1894..489ecb871e 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2007, /**/ 2006, /**/ diff --git a/src/vim9type.c b/src/vim9type.c index de1033c2e9..338aee14a7 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -874,8 +874,7 @@ check_type_maybe( { where_T func_where = where; - if (where.wt_kind == WT_METHOD) - func_where.wt_kind = WT_METHOD_RETURN; + func_where.wt_kind = WT_METHOD_RETURN; ret = check_type_maybe(expected->tt_member, actual->tt_member, FALSE, func_where); @@ -898,8 +897,7 @@ check_type_maybe( && i < actual->tt_argcount; ++i) { where_T func_where = where; - if (where.wt_kind == WT_METHOD) - func_where.wt_kind = WT_METHOD_ARG; + func_where.wt_kind = WT_METHOD_ARG; // Allow for using "any" argument type, lambda's have them. if (actual->tt_args[i] != &t_any && check_type( From b07b9dc4dafe2aad5ee752a51f06acacae210fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominique=20Pell=C3=A9?= Date: Mon, 9 Oct 2023 18:07:24 +0200 Subject: [PATCH 19/47] patch 9.0.2008: test: undofile left behind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: test: undofile left behind Solution: cleanup undofile fix: tmp file not deleted when running make test_undo Temporary file `.Xtestfile.txt.un~` was left running `make test_undo` and vim was configured with: ``` ./configure --with-features=normal --enable-gui=no --enable-terminal ``` closes: #13304 Signed-off-by: Christian Brabandt Co-authored-by: Dominique Pellé --- src/testdir/test_undo.vim | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/testdir/test_undo.vim b/src/testdir/test_undo.vim index 58563bde63..13c4990b8c 100644 --- a/src/testdir/test_undo.vim +++ b/src/testdir/test_undo.vim @@ -860,6 +860,7 @@ func Test_undo_after_write() call StopVimInTerminal(buf) call delete('Xtestfile.txt') + call delete('.Xtestfile.txt.un~') endfunc func Test_undo_range_normal() diff --git a/src/version.c b/src/version.c index 489ecb871e..56f317dd14 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2008, /**/ 2007, /**/ From 54844857fd6933fa4f6678e47610c4b9c9f7a091 Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Mon, 9 Oct 2023 18:12:31 +0200 Subject: [PATCH 20/47] patch 9.0.2009: cmdline-completion for comma-separated options wrong Problem: cmdline-completion for comma-separated options wrong Solution: Fix command-line expansions for options with filenames with commas Fix command-line expansions for options with filenames with commas Cmdline expansion for option values that take a comma-separated list of file names is currently not handling file names with commas as the commas are not escaped. For such options, the commas in file names need to be escaped (to differentiate from a comma that delimit the list items). The escaped comma is unescaped in `copy_option_part()` during option parsing. Fix as follows: - Cmdline completion for option values with comma-separated file/folder names will not start a new match when seeing `\\,` and will instead consider it as one value. - File/folder regex matching will strip the `\\` when seeing `\\,` to make sure it can match the correct files/folders. - The expanded value will escape `,` with `\\,`, similar to how spaces are escaped to make sure the option value is correct on the cmdline. This fix also takes into account the fact that Win32 Vim handles file name escaping differently. Typing '\,' for a file name results in it being handled literally but in other platforms '\,' is interpreted as a simple ',' and commas need to be escaped using '\\,' instead. Also, make sure this new logic only applies to comma-separated options like 'path'. Non-list options like 'set makeprg=' and regular ex commands like `:edit ` do not require escaping and will continue to work. Also fix up documentation to be clearer. The original docs are slightly misleading in how it discusses triple slashes for 'tags'. closes: #13303 related: #13301 Signed-off-by: Christian Brabandt Co-authored-by: Yee Cheng Chin --- runtime/doc/options.txt | 27 ++++++++++++--- src/cmdexpand.c | 30 +++++++++++++--- src/option.c | 10 ++++-- src/structs.h | 5 +-- src/testdir/test_cmdline.vim | 66 +++++++++++++++++++++++++++++++++--- src/testdir/test_options.vim | 46 ++++++++++++++++++++++++- src/version.c | 2 ++ 7 files changed, 168 insertions(+), 18 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index c518663904..cfef59de60 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -192,10 +192,27 @@ To include white space in a string option value it has to be preceded with a backslash. To include a backslash you have to use two. Effectively this means that the number of backslashes in an option value is halved (rounded down). +In options 'path', 'cdpath', and 'tags', spaces have to be preceded with three +backslashes instead for compatibility with version 3.0 where the options can +be separated by either commas or spaces. +Comma-separated options like 'backupdir' and 'tags' will also require commas +to be escaped with two backslashes, whereas this is not needed for +non-comma-separated ones like 'makeprg'. +When setting options using |:let| and |literal-string|, you need to use one +fewer layer of backslash. A few examples: > - :set tags=tags\ /usr/tags results in "tags /usr/tags" - :set tags=tags\\,file results in "tags\,file" - :set tags=tags\\\ file results in "tags\ file" + :set makeprg=make\ file results in "make file" + :let &makeprg='make file' (same as above) + :set makeprg=make\\\ file results in "make\ file" + :set tags=tags\ /usr/tags results in "tags" and "/usr/tags" + :set tags=tags\\\ file results in "tags file" + :let &tags='tags\ file' (same as above) + + :set makeprg=make,file results in "make,file" + :set makeprg=make\\,file results in "make\,file" + :set tags=tags,file results in "tags" and "file" + :set tags=tags\\,file results in "tags,file" + :let &tags='tags\,file' (same as above) The "|" character separates a ":set" command from a following command. To include the "|" in the option value, use "\|" instead. This example sets the @@ -8213,8 +8230,8 @@ A jump table for the options with a short description can be found at |Q_op|. |+emacs_tags|: "./tags,./TAGS,tags,TAGS") global or local to buffer |global-local| Filenames for the tag command, separated by spaces or commas. To - include a space or comma in a file name, precede it with a backslash - (see |option-backslash| about including spaces and backslashes). + include a space or comma in a file name, precede it with backslashes + (see |option-backslash| about including spaces/commas and backslashes). When a file name starts with "./", the '.' is replaced with the path of the current file. But only when the 'd' flag is not included in 'cpoptions'. Environment variables are expanded |:set_env|. Also see diff --git a/src/cmdexpand.c b/src/cmdexpand.c index b59610c02e..20f3069ce2 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -113,9 +113,10 @@ wildescape( for (int i = 0; i < numfiles; ++i) { // for ":set path=" we need to escape spaces twice - if (xp->xp_backslash == XP_BS_THREE) + if (xp->xp_backslash & XP_BS_THREE) { - p = vim_strsave_escaped(files[i], (char_u *)" "); + char *pat = (xp->xp_backslash & XP_BS_COMMA) ? " ," : " "; + p = vim_strsave_escaped(files[i], (char_u *)pat); if (p != NULL) { vim_free(files[i]); @@ -130,6 +131,18 @@ wildescape( #endif } } + else if (xp->xp_backslash & XP_BS_COMMA) + { + if (vim_strchr(files[i], ',') != NULL) + { + p = vim_strsave_escaped(files[i], (char_u *)","); + if (p != NULL) + { + vim_free(files[i]); + files[i] = p; + } + } + } #ifdef BACKSLASH_IN_FILENAME p = vim_strsave_fnameescape(files[i], vse_what); #else @@ -2730,14 +2743,23 @@ expand_files_and_dirs( for (i = 0; pat[i]; ++i) if (pat[i] == '\\') { - if (xp->xp_backslash == XP_BS_THREE + if (xp->xp_backslash & XP_BS_THREE && pat[i + 1] == '\\' && pat[i + 2] == '\\' && pat[i + 3] == ' ') STRMOVE(pat + i, pat + i + 3); - if (xp->xp_backslash == XP_BS_ONE + else if (xp->xp_backslash & XP_BS_ONE && pat[i + 1] == ' ') STRMOVE(pat + i, pat + i + 1); + else if ((xp->xp_backslash & XP_BS_COMMA) + && pat[i + 1] == '\\' + && pat[i + 2] == ',') + STRMOVE(pat + i, pat + i + 2); +#ifdef BACKSLASH_IN_FILENAME + else if ((xp->xp_backslash & XP_BS_COMMA) + && pat[i + 1] == ',') + STRMOVE(pat + i, pat + i + 1); +#endif } } diff --git a/src/option.c b/src/option.c index ae2ca3a341..b1e70c6fd2 100644 --- a/src/option.c +++ b/src/option.c @@ -7451,6 +7451,8 @@ set_context_in_set_cmd( else xp->xp_backslash = XP_BS_ONE; } + if (flags & P_COMMA) + xp->xp_backslash |= XP_BS_COMMA; } // For an option that is a list of file names, or comma/colon-separated @@ -7469,8 +7471,12 @@ set_context_in_set_cmd( s = p; while (s > xp->xp_pattern && *(s - 1) == '\\') --s; - if ((*p == ' ' && (xp->xp_backslash == XP_BS_THREE && (p - s) < 3)) - || (*p == ',' && (flags & P_COMMA) && ((p - s) % 1) == 0) + if ((*p == ' ' && ((xp->xp_backslash & XP_BS_THREE) && (p - s) < 3)) +#if defined(BACKSLASH_IN_FILENAME) + || (*p == ',' && (flags & P_COMMA) && (p - s) < 1) +#else + || (*p == ',' && (flags & P_COMMA) && (p - s) < 2) +#endif || (*p == ':' && (flags & P_COLON))) { xp->xp_pattern = p + 1; diff --git a/src/structs.h b/src/structs.h index ee688848e7..c7cf4128ca 100644 --- a/src/structs.h +++ b/src/structs.h @@ -631,8 +631,9 @@ typedef struct expand * values for xp_backslash */ #define XP_BS_NONE 0 // nothing special for backslashes -#define XP_BS_ONE 1 // uses one backslash before a space -#define XP_BS_THREE 2 // uses three backslashes before a space +#define XP_BS_ONE 0x1 // uses one backslash before a space +#define XP_BS_THREE 0x2 // uses three backslashes before a space +#define XP_BS_COMMA 0x4 // commas need to be escaped with a backslash /* * Variables shared between getcmdline(), redrawcmdline() and others. diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index a54c507387..e79fa72bba 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -1257,13 +1257,71 @@ func Test_cmdline_complete_various() mapclear delcom MyCmd + " Prepare for path completion + call mkdir('Xa b c', 'D') + defer delete('Xcomma,foobar.txt') + call writefile([], 'Xcomma,foobar.txt') + " completion for :set path= with multiple backslashes - call feedkeys(":set path=a\\\\\\ b\\\"\", 'xt') - call assert_equal('"set path=a\\\ b', @:) + call feedkeys(':set path=Xa\\\ b' .. "\\\"\", 'xt') + call assert_equal('"set path=Xa\\\ b\\\ c/', @:) + set path& " completion for :set dir= with a backslash - call feedkeys(":set dir=a\\ b\\\"\", 'xt') - call assert_equal('"set dir=a\ b', @:) + call feedkeys(':set dir=Xa\ b' .. "\\\"\", 'xt') + call assert_equal('"set dir=Xa\ b\ c/', @:) + set dir& + + " completion for :set tags= / set dictionary= with escaped commas + if has('win32') + " In Windows backslashes are rounded up, so both '\,' and '\\,' escape to + " '\,' + call feedkeys(':set dictionary=Xcomma\,foo' .. "\\\"\", 'xt') + call assert_equal('"set dictionary=Xcomma\,foobar.txt', @:) + + call feedkeys(':set tags=Xcomma\\,foo' .. "\\\"\", 'xt') + call assert_equal('"set tags=Xcomma\,foobar.txt', @:) + + call feedkeys(':set tags=Xcomma\\\,foo' .. "\\\"\", 'xt') + call assert_equal('"set tags=Xcomma\\\,foo', @:) " Didn't find a match + + " completion for :set dictionary= with escaped commas (same behavior, but + " different internal code path from 'set tags=' for escaping the output) + call feedkeys(':set tags=Xcomma\\,foo' .. "\\\"\", 'xt') + call assert_equal('"set tags=Xcomma\,foobar.txt', @:) + else + " In other platforms, backslashes are rounded down (since '\,' itself will + " be escaped into ','). As a result '\\,' and '\\\,' escape to '\,'. + call feedkeys(':set tags=Xcomma\,foo' .. "\\\"\", 'xt') + call assert_equal('"set tags=Xcomma\,foo', @:) " Didn't find a match + + call feedkeys(':set tags=Xcomma\\,foo' .. "\\\"\", 'xt') + call assert_equal('"set tags=Xcomma\\,foobar.txt', @:) + + call feedkeys(':set dictionary=Xcomma\\\,foo' .. "\\\"\", 'xt') + call assert_equal('"set dictionary=Xcomma\\,foobar.txt', @:) + + " completion for :set dictionary= with escaped commas (same behavior, but + " different internal code path from 'set tags=' for escaping the output) + call feedkeys(':set dictionary=Xcomma\\,foo' .. "\\\"\", 'xt') + call assert_equal('"set dictionary=Xcomma\\,foobar.txt', @:) + endif + set tags& + set dictionary& + + " completion for :set makeprg= with no escaped commas + call feedkeys(':set makeprg=Xcomma,foo' .. "\\\"\", 'xt') + call assert_equal('"set makeprg=Xcomma,foobar.txt', @:) + + if !has('win32') + " Cannot create file with backslash in file name in Windows, so only test + " this elsewhere. + defer delete('Xcomma\,fooslash.txt') + call writefile([], 'Xcomma\,fooslash.txt') + call feedkeys(':set makeprg=Xcomma\\,foo' .. "\\\"\", 'xt') + call assert_equal('"set makeprg=Xcomma\\,fooslash.txt', @:) + endif + set makeprg& " completion for the :py3 commands call feedkeys(":py3\\\"\", 'xt') diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index 2f6c8d2838..ba0808956d 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -314,6 +314,7 @@ func Test_set_completion() call feedkeys(":set cdpath=./\\\"\", 'tx') call assert_match(' ./samples/ ', @:) call assert_notmatch(' ./summarize.vim ', @:) + set cdpath& " Expand files and directories. call feedkeys(":set tags=./\\\"\", 'tx') @@ -321,7 +322,50 @@ func Test_set_completion() call feedkeys(":set tags=./\\\\ dif\\\"\", 'tx') call assert_equal('"set tags=./\\ diff diffexpr diffopt', @:) - set tags& + + " Expand files with spaces/commas in them. Make sure we delimit correctly. + " + " 'tags' allow for for spaces/commas to both act as delimiters, with actual + " spaces requiring double escape, and commas need a single escape. + " 'dictionary' is a normal comma-separated option where only commas act as + " delimiters, and both space/comma need one single escape. + " 'makeprg' is a non-comma-separated option. Commas don't need escape. + defer delete('Xfoo Xspace.txt') + defer delete('Xsp_dummy') + defer delete('Xbar,Xcomma.txt') + defer delete('Xcom_dummy') + call writefile([], 'Xfoo Xspace.txt') + call writefile([], 'Xsp_dummy') + call writefile([], 'Xbar,Xcomma.txt') + call writefile([], 'Xcom_dummy') + + call feedkeys(':set tags=./Xfoo\ Xsp' .. "\\\"\", 'tx') + call assert_equal('"set tags=./Xfoo\ Xsp_dummy', @:) + call feedkeys(':set tags=./Xfoo\\\ Xsp' .. "\\\"\", 'tx') + call assert_equal('"set tags=./Xfoo\\\ Xspace.txt', @:) + call feedkeys(':set dictionary=./Xfoo\ Xsp' .. "\\\"\", 'tx') + call assert_equal('"set dictionary=./Xfoo\ Xspace.txt', @:) + + call feedkeys(':set dictionary=./Xbar,Xcom' .. "\\\"\", 'tx') + call assert_equal('"set dictionary=./Xbar,Xcom_dummy', @:) + if has('win32') + " In Windows, '\,' is literal, see `:help filename-backslash`, so this + " means we treat it as one file name. + call feedkeys(':set dictionary=Xbar\,Xcom' .. "\\\"\", 'tx') + call assert_equal('"set dictionary=Xbar\,Xcomma.txt', @:) + else + " In other platforms, '\,' simply escape to ',', and indicate a delimiter + " to split into a separate file name. You need '\\,' to escape the comma + " as part of the file name. + call feedkeys(':set dictionary=Xbar\,Xcom' .. "\\\"\", 'tx') + call assert_equal('"set dictionary=Xbar\,Xcom_dummy', @:) + + call feedkeys(':set dictionary=Xbar\\,Xcom' .. "\\\"\", 'tx') + call assert_equal('"set dictionary=Xbar\\,Xcomma.txt', @:) + endif + call feedkeys(":set makeprg=./Xbar,Xcom\\\"\", 'tx') + call assert_equal('"set makeprg=./Xbar,Xcomma.txt', @:) + set tags& dictionary& makeprg& " Expanding the option names call feedkeys(":set \\\"\", 'xt') diff --git a/src/version.c b/src/version.c index 56f317dd14..458b46a371 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2009, /**/ 2008, /**/ From 41e6f7d6ba67b61d911f9b1d76325cd79224753d Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 11 Oct 2023 21:08:13 +0200 Subject: [PATCH 21/47] patch 9.0.2010: [security] use-after-free from buf_contents_changed() Problem: [security] use-after-free from buf_contents_changed() Solution: block autocommands Signed-off-by: Christian Brabandt --- src/buffer.c | 5 +++++ src/testdir/crash/editing_arg_idx_POC_1 | Bin 0 -> 398 bytes src/testdir/test_crash.vim | 9 +++++++++ src/version.c | 2 ++ 4 files changed, 16 insertions(+) create mode 100644 src/testdir/crash/editing_arg_idx_POC_1 diff --git a/src/buffer.c b/src/buffer.c index 93f9245f27..9ee74f54dd 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -6013,6 +6013,9 @@ buf_contents_changed(buf_T *buf) return TRUE; } + // We don't want to trigger autocommands now, they may have nasty + // side-effects like wiping buffers + block_autocmds(); if (ml_open(curbuf) == OK && readfile(buf->b_ffname, buf->b_fname, (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, @@ -6038,6 +6041,8 @@ buf_contents_changed(buf_T *buf) if (curbuf != newbuf) // safety check wipe_buffer(newbuf, FALSE); + unblock_autocmds(); + return differ; } diff --git a/src/testdir/crash/editing_arg_idx_POC_1 b/src/testdir/crash/editing_arg_idx_POC_1 new file mode 100644 index 0000000000000000000000000000000000000000..5d048d03405a31e268f30950dc11d9dc767103de GIT binary patch literal 398 zcmZwD!Ait15C-6Q&0W2Yy>wf2DbmwYi}(amL<%CBO(xw!n=DDn_NF)A!FTfFOZWty z*Th|sivKAC^TU_ny6>4fIaACu7b(;@t_>8u7Pjng+-^-{OUeeP;cAc-GI4DXQos=- zt0I`seSs-4iwd?JWEmwun+a literal 0 HcmV?d00001 diff --git a/src/testdir/test_crash.vim b/src/testdir/test_crash.vim index 9a80340c28..5cd07e2a3f 100644 --- a/src/testdir/test_crash.vim +++ b/src/testdir/test_crash.vim @@ -78,6 +78,14 @@ func Test_crash1() \ ' && echo "crash 9: [OK]" >> X_crash1_result.txt' .. "\") call TermWait(buf, 1000) + let file = 'crash/editing_arg_idx_POC_1' + let args = printf(cmn_args, vim, file) + call term_sendkeys(buf, args .. + \ ' || echo "crash 10: [OK]" >> X_crash1_result.txt' .. "\") + call TermWait(buf, 1000) + call delete('Xerr') + call delete('@') + " clean up exe buf .. "bw!" @@ -93,6 +101,7 @@ func Test_crash1() \ 'crash 7: [OK]', \ 'crash 8: [OK]', \ 'crash 9: [OK]', + \ 'crash 10: [OK]', \ ] call assert_equal(expected, getline(1, '$')) diff --git a/src/version.c b/src/version.c index 458b46a371..0479f2096d 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2010, /**/ 2009, /**/ From 4a82bdfaa8022402b1ca0f0000c94c47a13f1014 Mon Sep 17 00:00:00 2001 From: Martin Tournoij Date: Wed, 11 Oct 2023 21:20:06 +0200 Subject: [PATCH 22/47] patch 9.0.2011: INI files not detected Problem: INI files not detected Solution: detect uppercase .INI as dosini files It previo~1 only worked for lower-case .ini files, but upperc~1 .INI is also somewhat common on account of DOS' old 8.3 upperc~2 only filena~1. closes: #13316 Signed-off-by: Christian Brabandt Co-authored-by: Martin Tournoij --- runtime/filetype.vim | 2 +- src/testdir/test_filetype.vim | 2 +- src/version.c | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 3e7036cbbc..700fd6a61a 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1013,7 +1013,7 @@ au BufNewFile,BufRead ipf.conf,ipf6.conf,ipf.rules setf ipfilter au BufNewFile,BufRead *.4gl,*.4gh,*.m4gl setf fgl " .INI file for MSDOS -au BufNewFile,BufRead *.ini setf dosini +au BufNewFile,BufRead *.ini,*.INI setf dosini " SysV Inittab au BufNewFile,BufRead inittab setf inittab diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 25710116b0..78e04d3848 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -206,7 +206,7 @@ def s:GetFilenameChecks(): dict> dnsmasq: ['/etc/dnsmasq.conf', '/etc/dnsmasq.d/file', 'any/etc/dnsmasq.conf', 'any/etc/dnsmasq.d/file'], dockerfile: ['Containerfile', 'Dockerfile', 'dockerfile', 'file.Dockerfile', 'file.dockerfile', 'Dockerfile.debian', 'Containerfile.something'], dosbatch: ['file.bat'], - dosini: ['/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap', 'file.vbp'], + dosini: ['/etc/yum.conf', 'file.ini', 'npmrc', '.npmrc', 'php.ini', 'php.ini-5', 'php.ini-file', '/etc/yum.repos.d/file', 'any/etc/yum.conf', 'any/etc/yum.repos.d/file', 'file.wrap', 'file.vbp', 'ja2.ini', 'JA2.INI'], dot: ['file.dot', 'file.gv'], dracula: ['file.drac', 'file.drc', 'filelvs', 'filelpe', 'drac.file', 'lpe', 'lvs', 'some-lpe', 'some-lvs'], dtd: ['file.dtd'], diff --git a/src/version.c b/src/version.c index 0479f2096d..441acfd12f 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2011, /**/ 2010, /**/ From f8da32461969a657ce9f132f35ddbec4068d3296 Mon Sep 17 00:00:00 2001 From: Ernie Rael Date: Wed, 11 Oct 2023 21:22:12 +0200 Subject: [PATCH 23/47] patch 9.0.2012: Vim9: error message can be more accurate Problem: Vim9: error message can be more accurate Solution: Fix the error messages Fix message for some single use error messages. closes: #13312 Signed-off-by: Christian Brabandt Co-authored-by: Ernie Rael --- src/errors.h | 8 ++++---- src/eval.c | 8 ++++---- src/testdir/test_listdict.vim | 4 ++-- src/testdir/test_vim9_assign.vim | 2 +- src/version.c | 2 ++ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/errors.h b/src/errors.h index 2474e27d4c..50cf6698da 100644 --- a/src/errors.h +++ b/src/errors.h @@ -1771,8 +1771,8 @@ EXTERN char e_less_targets_than_list_items[] INIT(= N_("E687: Less targets than List items")); EXTERN char e_more_targets_than_list_items[] INIT(= N_("E688: More targets than List items")); -EXTERN char e_can_only_index_list_dictionary_or_blob[] - INIT(= N_("E689: Can only index a List, Dictionary or Blob")); +EXTERN char e_index_not_allowed_after_str_str[] + INIT(= N_("E689: Index not allowed after a %s: %s")); EXTERN char e_missing_in_after_for[] INIT(= N_("E690: Missing \"in\" after :for")); EXTERN char e_can_only_compare_list_with_list[] @@ -3081,8 +3081,8 @@ EXTERN char e_libsodium_decryption_failed_premature[] #ifdef FEAT_EVAL EXTERN char e_no_white_space_allowed_after_str_str[] INIT(= N_("E1202: No white space allowed after '%s': %s")); -EXTERN char e_dot_can_only_be_used_on_dictionary_str[] - INIT(= N_("E1203: Dot can only be used on a dictionary: %s")); +EXTERN char e_dot_not_allowed_after_str_str[] + INIT(= N_("E1203: Dot not allowed after a %s: %s")); #endif EXTERN char e_regexp_number_after_dot_pos_search_chr[] INIT(= N_("E1204: No Number allowed after .: '\\%%%c'")); diff --git a/src/eval.c b/src/eval.c index 93109effb9..8b26eb189f 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1375,9 +1375,9 @@ get_lval( && v_type != VAR_OBJECT && v_type != VAR_CLASS) { - // TODO: have a message with obj/class, not just dict, if (!quiet) - semsg(_(e_dot_can_only_be_used_on_dictionary_str), name); + semsg(_(e_dot_not_allowed_after_str_str), + vartype_name(v_type), name); return NULL; } if (v_type != VAR_LIST @@ -1386,9 +1386,9 @@ get_lval( && v_type != VAR_OBJECT && v_type != VAR_CLASS) { - // TODO: have a message with obj/class, not just dict/list/blob, if (!quiet) - emsg(_(e_can_only_index_list_dictionary_or_blob)); + semsg(_(e_index_not_allowed_after_str_str), + vartype_name(v_type), name); return NULL; } diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim index b7c8ad7346..09e73ef9ca 100644 --- a/src/testdir/test_listdict.vim +++ b/src/testdir/test_listdict.vim @@ -435,13 +435,13 @@ func Test_dict_assign() let n = 0 let n.key = 3 END - call v9.CheckScriptFailure(lines, 'E1203: Dot can only be used on a dictionary: n.key = 3') + call v9.CheckScriptFailure(lines, 'E1203: Dot not allowed after a number: n.key = 3') let lines =<< trim END vim9script var n = 0 n.key = 3 END - call v9.CheckScriptFailure(lines, 'E1203: Dot can only be used on a dictionary: n.key = 3') + call v9.CheckScriptFailure(lines, 'E1203: Dot not allowed after a number: n.key = 3') let lines =<< trim END var n = 0 n.key = 3 diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 07afadb0c8..3a187774ec 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -1278,7 +1278,7 @@ def Test_assignment_dict() var n: any n.key = 5 END - v9.CheckDefExecAndScriptFailure(lines, ['E1148:', 'E1203: Dot can only be used on a dictionary: n.key = 5'], 2) + v9.CheckDefExecAndScriptFailure(lines, ['E1148:', 'E1203: Dot not allowed after a number: n.key = 5'], 2) enddef def Test_assignment_local() diff --git a/src/version.c b/src/version.c index 441acfd12f..5b919069ca 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2012, /**/ 2011, /**/ From a634b92b969e1bcb47551a39bf4e11e41ba9aa17 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 11 Oct 2023 21:24:49 +0200 Subject: [PATCH 24/47] patch 9.0.2013: Unicode tables outdated Problem: Unicode tables outdated Solution: Update Unicode tables to v15.1 (released 23.09.2023) closes: #13311 Signed-off-by: Christian Brabandt --- src/mbyte.c | 8 +++++--- src/version.c | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/mbyte.c b/src/mbyte.c index 4951f78323..34592bd56c 100644 --- a/src/mbyte.c +++ b/src/mbyte.c @@ -1419,14 +1419,13 @@ utf_char2cells(int c) {0x2e80, 0x2e99}, {0x2e9b, 0x2ef3}, {0x2f00, 0x2fd5}, - {0x2ff0, 0x2ffb}, - {0x3000, 0x303e}, + {0x2ff0, 0x303e}, {0x3041, 0x3096}, {0x3099, 0x30ff}, {0x3105, 0x312f}, {0x3131, 0x318e}, {0x3190, 0x31e3}, - {0x31f0, 0x321e}, + {0x31ef, 0x321e}, {0x3220, 0x3247}, {0x3250, 0x4dbf}, {0x4e00, 0xa48c}, @@ -3152,8 +3151,10 @@ static convertStruct foldCase[] = {0x1fbe,0x1fbe,-1,-7173}, {0x1fc8,0x1fcb,1,-86}, {0x1fcc,0x1fcc,-1,-9}, + {0x1fd3,0x1fd3,-1,-7235}, {0x1fd8,0x1fd9,1,-8}, {0x1fda,0x1fdb,1,-100}, + {0x1fe3,0x1fe3,-1,-7219}, {0x1fe8,0x1fe9,1,-8}, {0x1fea,0x1feb,1,-112}, {0x1fec,0x1fec,-1,-7}, @@ -3210,6 +3211,7 @@ static convertStruct foldCase[] = {0xa7d0,0xa7d6,6,1}, {0xa7d8,0xa7f5,29,1}, {0xab70,0xabbf,1,-38864}, + {0xfb05,0xfb05,-1,1}, {0xff21,0xff3a,1,32}, {0x10400,0x10427,1,40}, {0x104b0,0x104d3,1,40}, diff --git a/src/version.c b/src/version.c index 5b919069ca..0f5a10e2fb 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2013, /**/ 2012, /**/ From c97b3febc82c1ccacf5f328ed0bd81c8b850e97d Mon Sep 17 00:00:00 2001 From: Ken Takata Date: Wed, 11 Oct 2023 21:27:06 +0200 Subject: [PATCH 25/47] patch 9.0.2013: confusing ifdefs in if_.c Problem: confusing ifdefs in if_.c Solution: refactor ifndefs to #ifdefs if_x: Avoid using #ifndef - #else - #endif Using #ifndef - #else - #endif is sometimes confusing. Use #ifdef - #else - #endif instead. closes: #13310 Signed-off-by: Christian Brabandt Co-authored-by: Ken Takata --- src/if_lua.c | 12 ++++---- src/if_mzsch.c | 24 ++++++++-------- src/if_perl.xs | 14 +++++----- src/if_python.c | 22 +++++++-------- src/if_python3.c | 72 +++++++++++++++++++++++++----------------------- src/if_ruby.c | 14 +++++----- src/if_tcl.c | 20 +++++++------- src/version.c | 2 ++ 8 files changed, 92 insertions(+), 88 deletions(-) diff --git a/src/if_lua.c b/src/if_lua.c index 65d265f388..159a6753e3 100644 --- a/src/if_lua.c +++ b/src/if_lua.c @@ -102,18 +102,18 @@ static void luaV_call_lua_func_free(void *state); #ifdef DYNAMIC_LUA -#ifndef MSWIN +#ifdef MSWIN +# define load_dll vimLoadLib +# define symbol_from_dll GetProcAddress +# define close_dll FreeLibrary +# define load_dll_error GetWin32Error +#else # include # define HANDLE void* # define load_dll(n) dlopen((n), RTLD_LAZY|RTLD_GLOBAL) # define symbol_from_dll dlsym # define close_dll dlclose # define load_dll_error dlerror -#else -# define load_dll vimLoadLib -# define symbol_from_dll GetProcAddress -# define close_dll FreeLibrary -# define load_dll_error GetWin32Error #endif // lauxlib diff --git a/src/if_mzsch.c b/src/if_mzsch.c index e44714acf8..9a9c48732b 100644 --- a/src/if_mzsch.c +++ b/src/if_mzsch.c @@ -1366,10 +1366,10 @@ mzscheme_buffer_free(buf_T *buf) bp = BUFFER_REF(buf); bp->buf = INVALID_BUFFER_VALUE; -#ifndef MZ_PRECISE_GC - scheme_gc_ptr_ok(bp); -#else +#ifdef MZ_PRECISE_GC scheme_free_immobile_box(buf->b_mzscheme_ref); +#else + scheme_gc_ptr_ok(bp); #endif buf->b_mzscheme_ref = NULL; MZ_GC_CHECK(); @@ -1391,10 +1391,10 @@ mzscheme_window_free(win_T *win) MZ_GC_REG(); wp = WINDOW_REF(win); wp->win = INVALID_WINDOW_VALUE; -#ifndef MZ_PRECISE_GC - scheme_gc_ptr_ok(wp); -#else +#ifdef MZ_PRECISE_GC scheme_free_immobile_box(win->w_mzscheme_ref); +#else + scheme_gc_ptr_ok(wp); #endif win->w_mzscheme_ref = NULL; MZ_GC_CHECK(); @@ -1921,10 +1921,10 @@ window_new(win_T *win) MZ_GC_REG(); self = scheme_malloc_fail_ok(scheme_malloc_tagged, sizeof(vim_mz_window)); CLEAR_POINTER(self); -#ifndef MZ_PRECISE_GC - scheme_dont_gc_ptr(self); // because win isn't visible to GC -#else +#ifdef MZ_PRECISE_GC win->w_mzscheme_ref = scheme_malloc_immobile_box(NULL); +#else + scheme_dont_gc_ptr(self); // because win isn't visible to GC #endif MZ_GC_CHECK(); WINDOW_REF(win) = self; @@ -2305,10 +2305,10 @@ buffer_new(buf_T *buf) MZ_GC_REG(); self = scheme_malloc_fail_ok(scheme_malloc_tagged, sizeof(vim_mz_buffer)); CLEAR_POINTER(self); -#ifndef MZ_PRECISE_GC - scheme_dont_gc_ptr(self); // because buf isn't visible to GC -#else +#ifdef MZ_PRECISE_GC buf->b_mzscheme_ref = scheme_malloc_immobile_box(NULL); +#else + scheme_dont_gc_ptr(self); // because buf isn't visible to GC #endif MZ_GC_CHECK(); BUFFER_REF(buf) = self; diff --git a/src/if_perl.xs b/src/if_perl.xs index 180fce64c4..312262ae3f 100644 --- a/src/if_perl.xs +++ b/src/if_perl.xs @@ -166,7 +166,13 @@ typedef int XSUBADDR_t; typedef int perl_key; # endif -# ifndef MSWIN +# ifdef MSWIN +# define PERL_PROC FARPROC +# define load_dll vimLoadLib +# define symbol_from_dll GetProcAddress +# define close_dll FreeLibrary +# define load_dll_error GetWin32Error +# else # include # define HANDLE void* # define PERL_PROC void* @@ -174,12 +180,6 @@ typedef int perl_key; # define symbol_from_dll dlsym # define close_dll dlclose # define load_dll_error dlerror -# else -# define PERL_PROC FARPROC -# define load_dll vimLoadLib -# define symbol_from_dll GetProcAddress -# define close_dll FreeLibrary -# define load_dll_error GetWin32Error # endif /* * Wrapper defines diff --git a/src/if_python.c b/src/if_python.c index af5d6c0a81..461ba52cf2 100644 --- a/src/if_python.c +++ b/src/if_python.c @@ -130,7 +130,12 @@ struct PyMethodDef { Py_ssize_t a; }; # define HINSTANCE long_u // for generating prototypes # endif -# ifndef MSWIN +# ifdef MSWIN +# define load_dll vimLoadLib +# define close_dll FreeLibrary +# define symbol_from_dll GetProcAddress +# define load_dll_error GetWin32Error +# else # include # define FARPROC void* # define HINSTANCE void* @@ -142,11 +147,6 @@ struct PyMethodDef { Py_ssize_t a; }; # define close_dll dlclose # define symbol_from_dll dlsym # define load_dll_error dlerror -# else -# define load_dll vimLoadLib -# define close_dll FreeLibrary -# define symbol_from_dll GetProcAddress -# define load_dll_error GetWin32Error # endif // This makes if_python.c compile without warnings against Python 2.5 @@ -496,14 +496,14 @@ static struct PYTHON_PROC *ptr; } python_funcname_table[] = { -# ifndef PY_SSIZE_T_CLEAN - {"PyArg_Parse", (PYTHON_PROC*)&dll_PyArg_Parse}, - {"PyArg_ParseTuple", (PYTHON_PROC*)&dll_PyArg_ParseTuple}, - {"Py_BuildValue", (PYTHON_PROC*)&dll_Py_BuildValue}, -# else +# ifdef PY_SSIZE_T_CLEAN {"_PyArg_Parse_SizeT", (PYTHON_PROC*)&dll_PyArg_Parse}, {"_PyArg_ParseTuple_SizeT", (PYTHON_PROC*)&dll_PyArg_ParseTuple}, {"_Py_BuildValue_SizeT", (PYTHON_PROC*)&dll_Py_BuildValue}, +# else + {"PyArg_Parse", (PYTHON_PROC*)&dll_PyArg_Parse}, + {"PyArg_ParseTuple", (PYTHON_PROC*)&dll_PyArg_ParseTuple}, + {"Py_BuildValue", (PYTHON_PROC*)&dll_Py_BuildValue}, # endif {"PyMem_Free", (PYTHON_PROC*)&dll_PyMem_Free}, {"PyMem_Malloc", (PYTHON_PROC*)&dll_PyMem_Malloc}, diff --git a/src/if_python3.c b/src/if_python3.c index 52027bd562..c3900892ea 100644 --- a/src/if_python3.c +++ b/src/if_python3.c @@ -104,6 +104,9 @@ #define PyString_FromString(repr) \ PyUnicode_Decode(repr, STRLEN(repr), ENC_OPT, ERRORS_DECODE_ARG) #define PyString_FromFormat PyUnicode_FromFormat +#ifdef PyUnicode_FromFormat +# define Py_UNICODE_USE_UCS_FUNCTIONS +#endif #ifndef PyInt_Check # define PyInt_Check(obj) PyLong_Check(obj) #endif @@ -134,7 +137,12 @@ static HINSTANCE hinstPy3 = 0; // Instance of python.dll #if defined(DYNAMIC_PYTHON3) || defined(PROTO) -# ifndef MSWIN +# ifdef MSWIN +# define load_dll vimLoadLib +# define close_dll FreeLibrary +# define symbol_from_dll GetProcAddress +# define load_dll_error GetWin32Error +# else # include # define FARPROC void* # if defined(PY_NO_RTLD_GLOBAL) && defined(PY3_NO_RTLD_GLOBAL) @@ -145,11 +153,6 @@ static HINSTANCE hinstPy3 = 0; // Instance of python.dll # define close_dll dlclose # define symbol_from_dll dlsym # define load_dll_error dlerror -# else -# define load_dll vimLoadLib -# define close_dll FreeLibrary -# define symbol_from_dll GetProcAddress -# define load_dll_error GetWin32Error # endif /* * Wrapper defines @@ -216,14 +219,14 @@ static HINSTANCE hinstPy3 = 0; // Instance of python.dll # define PyObject_GetItem py3_PyObject_GetItem # define PyObject_IsTrue py3_PyObject_IsTrue # define PyModule_GetDict py3_PyModule_GetDict -# ifndef USE_LIMITED_API +# ifdef USE_LIMITED_API +# define Py_CompileString py3_Py_CompileString +# define PyEval_EvalCode py3_PyEval_EvalCode +# else # undef PyRun_SimpleString # define PyRun_SimpleString py3_PyRun_SimpleString # undef PyRun_String # define PyRun_String py3_PyRun_String -# else -# define Py_CompileString py3_Py_CompileString -# define PyEval_EvalCode py3_PyEval_EvalCode # endif # define PyObject_GetAttrString py3_PyObject_GetAttrString # define PyObject_HasAttrString py3_PyObject_HasAttrString @@ -321,15 +324,14 @@ static HINSTANCE hinstPy3 = 0; // Instance of python.dll # define PyType_GenericNew py3_PyType_GenericNew # undef PyUnicode_FromString # define PyUnicode_FromString py3_PyUnicode_FromString -# ifndef PyUnicode_FromFormat -# define PyUnicode_FromFormat py3_PyUnicode_FromFormat -# else -# define Py_UNICODE_USE_UCS_FUNCTIONS +# ifdef Py_UNICODE_USE_UCS_FUNCTIONS # ifdef Py_UNICODE_WIDE # define PyUnicodeUCS4_FromFormat py3_PyUnicodeUCS4_FromFormat # else # define PyUnicodeUCS2_FromFormat py3_PyUnicodeUCS2_FromFormat # endif +# else +# define PyUnicode_FromFormat py3_PyUnicode_FromFormat # endif # undef PyUnicode_Decode # define PyUnicode_Decode py3_PyUnicode_Decode @@ -388,12 +390,12 @@ static void (*py3_Py_Finalize)(void); static void (*py3_PyErr_SetString)(PyObject *, const char *); static void (*py3_PyErr_SetObject)(PyObject *, PyObject *); static int (*py3_PyErr_ExceptionMatches)(PyObject *); -# ifndef USE_LIMITED_API -static int (*py3_PyRun_SimpleString)(char *); -static PyObject* (*py3_PyRun_String)(char *, int, PyObject *, PyObject *); -# else +# ifdef USE_LIMITED_API static PyObject* (*py3_Py_CompileString)(const char *, const char *, int); static PyObject* (*py3_PyEval_EvalCode)(PyObject *co, PyObject *globals, PyObject *locals); +# else +static int (*py3_PyRun_SimpleString)(char *); +static PyObject* (*py3_PyRun_String)(char *, int, PyObject *, PyObject *); # endif static PyObject* (*py3_PyObject_GetAttrString)(PyObject *, const char *); static int (*py3_PyObject_HasAttrString)(PyObject *, const char *); @@ -430,14 +432,14 @@ static int (*py3_PyType_GetFlags)(PyTypeObject *o); static int (*py3_PyType_Ready)(PyTypeObject *type); static int (*py3_PyDict_SetItemString)(PyObject *dp, char *key, PyObject *item); static PyObject* (*py3_PyUnicode_FromString)(const char *u); -# ifndef Py_UNICODE_USE_UCS_FUNCTIONS -static PyObject* (*py3_PyUnicode_FromFormat)(const char *u, ...); -# else +# ifdef Py_UNICODE_USE_UCS_FUNCTIONS # ifdef Py_UNICODE_WIDE static PyObject* (*py3_PyUnicodeUCS4_FromFormat)(const char *u, ...); # else static PyObject* (*py3_PyUnicodeUCS2_FromFormat)(const char *u, ...); # endif +# else +static PyObject* (*py3_PyUnicode_FromFormat)(const char *u, ...); # endif static PyObject* (*py3_PyUnicode_Decode)(const char *u, Py_ssize_t size, const char *encoding, const char *errors); @@ -594,12 +596,12 @@ static struct {"PyErr_SetString", (PYTHON_PROC*)&py3_PyErr_SetString}, {"PyErr_SetObject", (PYTHON_PROC*)&py3_PyErr_SetObject}, {"PyErr_ExceptionMatches", (PYTHON_PROC*)&py3_PyErr_ExceptionMatches}, -# ifndef USE_LIMITED_API - {"PyRun_SimpleString", (PYTHON_PROC*)&py3_PyRun_SimpleString}, - {"PyRun_String", (PYTHON_PROC*)&py3_PyRun_String}, -# else +# ifdef USE_LIMITED_API {"Py_CompileString", (PYTHON_PROC*)&py3_Py_CompileString}, {"PyEval_EvalCode", (PYTHON_PROC*)&PyEval_EvalCode}, +# else + {"PyRun_SimpleString", (PYTHON_PROC*)&py3_PyRun_SimpleString}, + {"PyRun_String", (PYTHON_PROC*)&py3_PyRun_String}, # endif {"PyObject_GetAttrString", (PYTHON_PROC*)&py3_PyObject_GetAttrString}, {"PyObject_HasAttrString", (PYTHON_PROC*)&py3_PyObject_HasAttrString}, @@ -668,14 +670,14 @@ static struct # endif {"PyUnicode_CompareWithASCIIString", (PYTHON_PROC*)&py3_PyUnicode_CompareWithASCIIString}, {"PyUnicode_AsUTF8String", (PYTHON_PROC*)&py3_PyUnicode_AsUTF8String}, -# ifndef Py_UNICODE_USE_UCS_FUNCTIONS - {"PyUnicode_FromFormat", (PYTHON_PROC*)&py3_PyUnicode_FromFormat}, -# else +# ifdef Py_UNICODE_USE_UCS_FUNCTIONS # ifdef Py_UNICODE_WIDE {"PyUnicodeUCS4_FromFormat", (PYTHON_PROC*)&py3_PyUnicodeUCS4_FromFormat}, # else {"PyUnicodeUCS2_FromFormat", (PYTHON_PROC*)&py3_PyUnicodeUCS2_FromFormat}, # endif +# else + {"PyUnicode_FromFormat", (PYTHON_PROC*)&py3_PyUnicode_FromFormat}, # endif {"PyBytes_AsString", (PYTHON_PROC*)&py3_PyBytes_AsString}, {"PyBytes_AsStringAndSize", (PYTHON_PROC*)&py3_PyBytes_AsStringAndSize}, @@ -1093,13 +1095,7 @@ static struct PyModuleDef vimmodule; */ #include "if_py_both.h" -#ifndef USE_LIMITED_API -# if PY_VERSION_HEX >= 0x030300f0 -# define PY_UNICODE_GET_UTF8_CHARS(obj) PyUnicode_AsUTF8AndSize(obj, NULL) -# else -# define PY_UNICODE_GET_UTF8_CHARS _PyUnicode_AsString -# endif -#else +#ifdef USE_LIMITED_API # if Py_LIMITED_API >= 0x030A0000 # define PY_UNICODE_GET_UTF8_CHARS(obj) PyUnicode_AsUTF8AndSize(obj, NULL) # else @@ -1131,6 +1127,12 @@ static char* PY_UNICODE_GET_UTF8_CHARS(PyObject* str) return py3_unicode_utf8_chars; } # endif +#else // !USE_LIMITED_API +# if PY_VERSION_HEX >= 0x030300f0 +# define PY_UNICODE_GET_UTF8_CHARS(obj) PyUnicode_AsUTF8AndSize(obj, NULL) +# else +# define PY_UNICODE_GET_UTF8_CHARS _PyUnicode_AsString +# endif #endif // NOTE: Must always be used at the start of a block, since it declares "name". diff --git a/src/if_ruby.c b/src/if_ruby.c index dd0f5db443..a921205255 100644 --- a/src/if_ruby.c +++ b/src/if_ruby.c @@ -174,7 +174,13 @@ #include "version.h" #ifdef DYNAMIC_RUBY -# if !defined(MSWIN) // must come after including vim.h, where it is defined +# ifdef MSWIN // must come after including vim.h, where it is defined +# define RUBY_PROC FARPROC +# define load_dll vimLoadLib +# define symbol_from_dll GetProcAddress +# define close_dll FreeLibrary +# define load_dll_error GetWin32Error +# else # include # define HINSTANCE void* # define RUBY_PROC void* @@ -182,12 +188,6 @@ # define symbol_from_dll dlsym # define close_dll dlclose # define load_dll_error dlerror -# else -# define RUBY_PROC FARPROC -# define load_dll vimLoadLib -# define symbol_from_dll GetProcAddress -# define close_dll FreeLibrary -# define load_dll_error GetWin32Error # endif #endif diff --git a/src/if_tcl.c b/src/if_tcl.c index 1882b41113..d3d46395ec 100644 --- a/src/if_tcl.c +++ b/src/if_tcl.c @@ -160,7 +160,13 @@ static struct ref refsdeleted; // dummy object for deleted ref list typedef int HANDLE; # endif -# ifndef MSWIN +# ifdef MSWIN +# define TCL_PROC FARPROC +# define load_dll vimLoadLib +# define symbol_from_dll GetProcAddress +# define close_dll FreeLibrary +# define load_dll_error GetWin32Error +# else # include # define HANDLE void* # define TCL_PROC void* @@ -168,12 +174,6 @@ typedef int HANDLE; # define symbol_from_dll dlsym # define close_dll dlclose # define load_dll_error dlerror -# else -# define TCL_PROC FARPROC -# define load_dll vimLoadLib -# define symbol_from_dll GetProcAddress -# define close_dll FreeLibrary -# define load_dll_error GetWin32Error # endif /* @@ -242,10 +242,10 @@ static char *find_executable_arg = NULL; void vim_tcl_init(char *arg) { -#ifndef DYNAMIC_TCL - Tcl_FindExecutable(arg); -#else +#ifdef DYNAMIC_TCL find_executable_arg = arg; +#else + Tcl_FindExecutable(arg); #endif } diff --git a/src/version.c b/src/version.c index 0f5a10e2fb..dd64eb3699 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2014, /**/ 2013, /**/ From 4c8da025ef8140168b7a09d9fe922ce4bb40f19d Mon Sep 17 00:00:00 2001 From: Ernie Rael Date: Wed, 11 Oct 2023 21:35:11 +0200 Subject: [PATCH 26/47] patch 9.0.2015: Vim9: does not handle islocked() from a method correctly Problem: Vim9: does not handle islocked() from a method correctly Solution: Handle islocked() builtin from a method. - Setup `lval_root` from `f_islocked()`. - Add function `fill_exec_lval_root()` to get info about executing method. - `sync_root` added in get_lval to handle method member access. - Conservative approach to reference counting. closes: #13309 Signed-off-by: Christian Brabandt Co-authored-by: Ernie Rael --- src/eval.c | 79 ++++++++--- src/evalfunc.c | 110 +++++++++++++- src/proto/vim9execute.pro | 1 + src/structs.h | 5 +- src/testdir/test_vim9_class.vim | 244 +++++++++++++++++++++++++++++++- src/version.c | 2 + src/vim9execute.c | 46 +++++- 7 files changed, 461 insertions(+), 26 deletions(-) diff --git a/src/eval.c b/src/eval.c index 8b26eb189f..4da6246ace 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1050,11 +1050,14 @@ flag_string_T glv_flag_strings[] = { * execute_instructions: ISN_LOCKUNLOCK - sets lval_root from stack. */ static void -get_lval_root(lval_T *lp, lval_root_T *lr) +fill_lval_from_lval_root(lval_T *lp, lval_root_T *lr) { #ifdef LOG_LOCKVAR - ch_log(NULL, "LKVAR: get_lval_root(): name %s", lp->ll_name); + ch_log(NULL, "LKVAR: fill_lval_from_lval_root(): name %s, tv %p", + lp->ll_name, (void*)lr->lr_tv); #endif + if (lr->lr_tv == NULL) + return; if (!lr->lr_is_arg && lr->lr_tv->v_type == VAR_CLASS) { if (lr->lr_tv->vval.v_class != NULL) @@ -1177,15 +1180,14 @@ get_lval( #ifdef LOG_LOCKVAR if (lval_root == NULL) - ch_log(NULL, - "LKVAR: get_lval(): name %s, lval_root (nil)", name); + ch_log(NULL, "LKVAR: get_lval(): name: %s, lval_root (nil)", name); else - ch_log(NULL, - "LKVAR: get_lval(): name %s, lr_tv %p lr_is_arg %d", - name, (void*)lval_root->lr_tv, lval_root->lr_is_arg); + ch_log(NULL, "LKVAR: get_lval(): name: %s, lr_tv %p lr_is_arg %d", + name, (void*)lval_root->lr_tv, lval_root->lr_is_arg); char buf[80]; - ch_log(NULL, "LKVAR: ...: GLV flags %s", + ch_log(NULL, "LKVAR: ...: GLV flags: %s", flags_tostring(flags, glv_flag_strings, buf, sizeof(buf))); + int log_sync_root_key = FALSE; #endif // Clear everything in "lp". @@ -1324,20 +1326,26 @@ get_lval( } } - // Without [idx] or .key we are done. - if ((*p != '[' && *p != '.')) + int sync_root = FALSE; + if (vim9script && lval_root != NULL) + { + cl_exec = lval_root->lr_cl_exec; + sync_root = lval_root->lr_sync_root; + } + + // Without [idx] or .key we are done, unless doing sync_root. + if (*p != '[' && *p != '.' && (*name == NUL || !sync_root)) { if (lval_root != NULL) - get_lval_root(lp, lval_root); + fill_lval_from_lval_root(lp, lval_root); return p; } - if (vim9script && lval_root != NULL) + if (vim9script && lval_root != NULL && lval_root->lr_tv != NULL) { // using local variable lp->ll_tv = lval_root->lr_tv; v = NULL; - cl_exec = lval_root->lr_cl_exec; } else { @@ -1367,7 +1375,7 @@ get_lval( */ var1.v_type = VAR_UNKNOWN; var2.v_type = VAR_UNKNOWN; - while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) + while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.') || sync_root) { vartype_T v_type = lp->ll_tv->v_type; @@ -1407,6 +1415,10 @@ get_lval( emsg(_(e_slice_must_come_last)); return NULL; } +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: get_lval() loop: p: %s, type: %s", p, + vartype_name(v_type)); +#endif if (vim9script && lp->ll_valtype == NULL && v != NULL @@ -1417,11 +1429,29 @@ get_lval( // Vim9 script local variable: get the type if (sv != NULL) + { lp->ll_valtype = sv->sv_type; +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: ... loop: vim9 assign type: %s", + vartype_name(lp->ll_valtype->tt_type)); +#endif + } } len = -1; - if (*p == '.') + if (sync_root) + { + // For example, the first token is a member variable name and + // lp->ll_tv is a class/object. + // Process it directly without looking for "[idx]" or ".name". + key = name; + sync_root = FALSE; // only first time through +#ifdef LOG_LOCKVAR + log_sync_root_key = TRUE; + ch_log(NULL, "LKVAR: ... loop: name: %s, sync_root", name); +#endif + } + else if (*p == '.') { key = p + 1; for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) @@ -1512,6 +1542,17 @@ get_lval( // Skip to past ']'. ++p; } +#ifdef LOG_LOCKVAR + if (log_sync_root_key) + ch_log(NULL, "LKVAR: ... loop: p: %s, sync_root key: %s", p, + key); + else if (len == -1) + ch_log(NULL, "LKVAR: ... loop: p: %s, '[' key: %s", p, + empty1 ? ":" : (char*)tv_get_string(&var1)); + else + ch_log(NULL, "LKVAR: ... loop: p: %s, '.' key: %s", p, key); + log_sync_root_key = FALSE; +#endif if (v_type == VAR_DICT) { @@ -1700,8 +1741,14 @@ get_lval( lp->ll_list = NULL; class_T *cl; - if (v_type == VAR_OBJECT && lp->ll_tv->vval.v_object != NULL) + if (v_type == VAR_OBJECT) { + if (lp->ll_tv->vval.v_object == NULL) + { + if (!quiet) + emsg(_(e_using_null_object)); + return NULL; + } cl = lp->ll_tv->vval.v_object->obj_class; lp->ll_object = lp->ll_tv->vval.v_object; } diff --git a/src/evalfunc.c b/src/evalfunc.c index b840220bd0..5fccf5270c 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -7307,6 +7307,83 @@ f_invert(typval_T *argvars, typval_T *rettv) rettv->vval.v_number = ~tv_get_number_chk(&argvars[0], NULL); } +/* + * Free resources in lval_root allocated by fill_exec_lval_root(). + */ + static void +free_lval_root(lval_root_T *root) +{ + if (root->lr_tv != NULL) + free_tv(root->lr_tv); + class_unref(root->lr_cl_exec); + root->lr_tv = NULL; + root->lr_cl_exec = NULL; +} + +/* + * This is used if executing in a method, the argument string is a + * variable/item expr/reference. If it starts with a potential class/object + * variable then return OK, may get later errors in get_lval. + * + * Adjust "root" as needed. Note that name may change (for example to skip + * "this") and is returned. lr_tv may be changed or freed. + * + * Always returns OK. + * Free resources and return FAIL if the root should not be used. Otherwise OK. + */ + + static int +fix_variable_reference_lval_root(lval_root_T *root, char_u **p_name) +{ + char_u *name = *p_name; + char_u *end; + dictitem_T *di; + + // Only set lr_sync_root and lr_tv if the name is an object/class + // reference: object ("this.") or class because name is class variable. + if (root->lr_tv->v_type == VAR_OBJECT) + { + if (STRNCMP("this.", name, 5) == 0) + { + name += 5; + root->lr_sync_root = TRUE; + } + else if (STRCMP("this", name) == 0) + { + name += 4; + root->lr_sync_root = TRUE; + } + } + if (!root->lr_sync_root) // not object member, try class member + { + // Explicitly check if the name is a class member. + // If it's not then do nothing. + for (end = name; ASCII_ISALNUM(*end) || *end == '_'; ++end) + ; + if (class_member_lookup(root->lr_cl_exec, name, end - name, NULL) + != NULL) + { + // Using a class, so reference the class tv. + di = find_var(root->lr_cl_exec->class_name, NULL, FALSE); + if (di != NULL) + { + // replace the lr_tv + clear_tv(root->lr_tv); + copy_tv(&di->di_tv, root->lr_tv); + root->lr_sync_root = TRUE; + } + } + } + if (!root->lr_sync_root) + { + free_tv(root->lr_tv); + root->lr_tv = NULL; // Not a member variable + } + *p_name = name; + // If FAIL, then must free_lval_root(root); + return OK; +} + /* * "islocked()" function */ @@ -7322,9 +7399,34 @@ f_islocked(typval_T *argvars, typval_T *rettv) if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; - end = get_lval(tv_get_string(&argvars[0]), NULL, &lv, FALSE, FALSE, + char_u *name = tv_get_string(&argvars[0]); +#ifdef LOG_LOCKVAR + ch_log(NULL, "LKVAR: f_islocked(): name: %s", name); +#endif + + lval_root_T aroot; // fully initialized in fill_exec_lval_root + lval_root_T *root = NULL; + + // Set up lval_root if executing in a method. + if (fill_exec_lval_root(&aroot) == OK) + { + // Almost always produces a valid lval_root since lr_cl_exec is used + // for access verification, lr_tv may be set to NULL. + char_u *tname = name; + if (fix_variable_reference_lval_root(&aroot, &tname) == OK) + { + name = tname; + root = &aroot; + } + } + + lval_root_T *lval_root_save = lval_root; + lval_root = root; + end = get_lval(name, NULL, &lv, FALSE, FALSE, GLV_NO_AUTOLOAD | GLV_READ_ONLY | GLV_NO_DECL, FNE_CHECK_START); + lval_root = lval_root_save; + if (end != NULL && lv.ll_name != NULL) { if (*end != NUL) @@ -7347,6 +7449,10 @@ f_islocked(typval_T *argvars, typval_T *rettv) || tv_islocked(&di->di_tv)); } } + else if (lv.ll_is_root) + { + rettv->vval.v_number = tv_islocked(lv.ll_tv); + } else if (lv.ll_object != NULL) { typval_T *tv = ((typval_T *)(lv.ll_object + 1)) + lv.ll_oi; @@ -7376,6 +7482,8 @@ f_islocked(typval_T *argvars, typval_T *rettv) } } + if (root != NULL) + free_lval_root(root); clear_lval(&lv); } diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro index 7f8e5fd306..15beb759fc 100644 --- a/src/proto/vim9execute.pro +++ b/src/proto/vim9execute.pro @@ -4,6 +4,7 @@ void update_has_breakpoint(ufunc_T *ufunc); int funcstack_check_refcount(funcstack_T *funcstack); int set_ref_in_funcstacks(int copyID); int in_def_function(void); +int fill_exec_lval_root(lval_root_T *lr); ectx_T *clear_current_ectx(void); void restore_current_ectx(ectx_T *ectx); int add_defer_function(char_u *name, int argcount, typval_T *argvars); diff --git a/src/structs.h b/src/structs.h index c7cf4128ca..5131858741 100644 --- a/src/structs.h +++ b/src/structs.h @@ -4604,13 +4604,16 @@ typedef struct lval_S } lval_T; /** - * This may be used to specify the base type that get_lval() uses when + * This may be used to specify the base typval that get_lval() uses when * following a chain, for example a[idx1][idx2]. + * The lr_sync_root flags signals get_lval that the first time through + * the indexing loop, skip handling '.' and '[idx]'. */ typedef struct lval_root_S { typval_T *lr_tv; class_T *lr_cl_exec; // executing class for access checking int lr_is_arg; + int lr_sync_root; } lval_root_T; // Structure used to save the current state. Used when executing Normal mode diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index ce7d5f7faa..eead19283a 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -1704,8 +1704,7 @@ def Test_class_member() var obj: A obj.val = "" END - # FIXME(in source): this should give E1360 as well! - v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object but got string', 7) + v9.CheckSourceFailure(lines, 'E1360: Using a null object', 7) # Test for accessing a member on a null object, at script level lines =<< trim END @@ -4259,8 +4258,249 @@ def Test_lockvar_islocked() assert_equal(0, islocked("C.c1[0]")) END v9.CheckSourceSuccess(lines) + + # Do islocked() from an object method + # and then from a class method + lines =<< trim END + vim9script + + var l0o0 = [ [0], [1], [2]] + var l0o1 = [ [10], [11], [12]] + var l0c0 = [[120], [121], [122]] + var l0c1 = [[130], [131], [132]] + + class C0 + this.o0: list> = l0o0 + this.o1: list> = l0o1 + static c0: list> = l0c0 + static c1: list> = l0c1 + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + + var l2o0 = [[20000], [20001], [20002]] + var l2o1 = [[20010], [20011], [20012]] + var l2c0 = [[20120], [20121], [20122]] + var l2c1 = [[20130], [20131], [20132]] + + class C2 + this.o0: list> = l2o0 + this.o1: list> = l2o1 + static c0: list> = l2c0 + static c1: list> = l2c1 + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + + var obj0 = C0.new() + var obj2 = C2.new() + + var l = [ obj0, null_object, obj2 ] + + # lock list, object func access through script var expr + assert_equal(0, obj0.Islocked("l[0].o0")) + assert_equal(0, obj0.Islocked("l[0].o0[2]")) + lockvar l0o0 + assert_equal(1, obj0.Islocked("l[0].o0")) + assert_equal(1, obj0.Islocked("l[0].o0[2]")) + + #echo "check-b" obj2.Islocked("l[1].o1") # NULL OBJECT + + # lock list element, object func access through script var expr + lockvar l0o1[1] + assert_equal(0, obj0.Islocked("this.o1[0]")) + assert_equal(1, obj0.Islocked("this.o1[1]")) + + assert_equal(0, obj0.Islocked("this.o1")) + lockvar l0o1 + assert_equal(1, obj0.Islocked("this.o1")) + unlockvar l0o1 + + lockvar l0c1[1] + + # static by class name member expr from same class + assert_equal(0, obj0.Islocked("C0.c1[0]")) + assert_equal(1, obj0.Islocked("C0.c1[1]")) + # static by bare name member expr from same class + assert_equal(0, obj0.Islocked("c1[0]")) + assert_equal(1, obj0.Islocked("c1[1]")) + + # static by class name member expr from other class + assert_equal(0, obj2.Islocked("C0.c1[0]")) + assert_equal(1, obj2.Islocked("C0.c1[1]")) + # static by bare name member expr from other class + assert_equal(0, obj2.Islocked("c1[0]")) + assert_equal(0, obj2.Islocked("c1[1]")) + + + # static by bare name in same class + assert_equal(0, obj0.Islocked("c0")) + lockvar l0c0 + assert_equal(1, obj0.Islocked("c0")) + + # + # similar stuff, but use static method + # + + unlockvar l0o0 + + # lock list, object func access through script var expr + assert_equal(0, C0.SIslocked("l[0].o0")) + assert_equal(0, C0.SIslocked("l[0].o0[2]")) + lockvar l0o0 + assert_equal(1, C0.SIslocked("l[0].o0")) + assert_equal(1, C0.SIslocked("l[0].o0[2]")) + + unlockvar l0o1 + + # can't access "this" from class method + try + C0.SIslocked("this.o1[0]") + call assert_0(1, '"C0.SIslocked("this.o1[0]")" should have failed') + catch + call assert_exception('E121: Undefined variable: this') + endtry + + lockvar l0c1[1] + + # static by class name member expr from same class + assert_equal(0, C0.SIslocked("C0.c1[0]")) + assert_equal(1, C0.SIslocked("C0.c1[1]")) + # static by bare name member expr from same class + assert_equal(0, C0.SIslocked("c1[0]")) + assert_equal(1, C0.SIslocked("c1[1]")) + + # static by class name member expr from other class + assert_equal(0, C2.SIslocked("C0.c1[0]")) + assert_equal(1, C2.SIslocked("C0.c1[1]")) + # static by bare name member expr from other class + assert_equal(0, C2.SIslocked("c1[0]")) + assert_equal(0, C2.SIslocked("c1[1]")) + + + # static by bare name in same class + unlockvar l0c0 + assert_equal(0, C0.SIslocked("c0")) + lockvar l0c0 + assert_equal(1, C0.SIslocked("c0")) + END + v9.CheckSourceSuccess(lines) + + # Check islocked class/object from various places. + lines =<< trim END + vim9script + + class C + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + + # object method + assert_equal(0, obj.Islocked("this")) + assert_equal(0, obj.Islocked("C")) + + # class method + ### assert_equal(0, C.SIslocked("this")) + assert_equal(0, C.SIslocked("C")) + + #script level + var v: number + v = islocked("C") + assert_equal(0, v) + v = islocked("obj") + assert_equal(0, v) + END + v9.CheckSourceSuccess(lines) +enddef + +def Test_lockvar_islocked_notfound() + # Try non-existent things + var lines =<< trim END + vim9script + + class C + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + assert_equal(-1, obj.Islocked("anywhere")) + assert_equal(-1, C.SIslocked("notanywhere")) + END + v9.CheckSourceSuccess(lines) + + # Something not found of the form "name1.name2" is an error + lines =<< trim END + vim9script + + islocked("one.two") + END + v9.CheckSourceFailure(lines, 'E121: Undefined variable: one') + + lines =<< trim END + vim9script + + class C + this.val = { key: "value" } + def Islocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + obj.Islocked("this.val.not_there")) + END + v9.CheckSourceFailure(lines, 'E716: Key not present in Dictionary: "not_there"') + lines =<< trim END + vim9script + + class C + def Islocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + obj.Islocked("this.notobjmember") END + v9.CheckSourceFailure(lines, 'E1326: Variable not found on object "C": notobjmember') + + # access a script variable through methods + lines =<< trim END + vim9script + + var l = [1] + class C + def Islocked(arg: string): number + return islocked(arg) + enddef + static def SIslocked(arg: string): number + return islocked(arg) + enddef + endclass + var obj = C.new() + assert_equal(0, obj.Islocked("l")) + assert_equal(0, C.SIslocked("l")) + lockvar l + assert_equal(1, obj.Islocked("l")) + assert_equal(1, C.SIslocked("l")) + END + v9.CheckSourceSuccess(lines) enddef " Test for a private object method diff --git a/src/version.c b/src/version.c index dd64eb3699..61f6289e28 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2015, /**/ 2014, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 826282241b..a89bcd19b4 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -925,6 +925,41 @@ in_def_function(void) return current_ectx != NULL; } +/* + * If executing a class/object method, then fill in the lval_T. + * Set lr_tv to the executing item, and lr_exec_class to the executing class; + * use free_tv and class_unref when finished with the lval_root. + * For use by builtin functions. + * + * Return FAIL and do nothing if not executing in a class; otherwise OK. + */ + int +fill_exec_lval_root(lval_root_T *root) +{ + ectx_T *ectx = current_ectx; + if (ectx != NULL) + { + dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + + current_ectx->ec_dfunc_idx; + ufunc_T *ufunc = dfunc->df_ufunc; + if (ufunc->uf_class != NULL) // executing a method? + { + typval_T *tv = alloc_tv(); + if (tv != NULL) + { + CLEAR_POINTER(root); + root->lr_tv = tv; + copy_tv(STACK_TV_VAR(0), root->lr_tv); + root->lr_cl_exec = ufunc->uf_class; + ++root->lr_cl_exec->class_refcount; + return OK; + } + } + } + + return FAIL; +} + /* * Clear "current_ectx" and return the previous value. To be used when calling * a user function. @@ -4185,21 +4220,20 @@ exec_instructions(ectx_T *ectx) case ISN_LOCKUNLOCK: { - lval_root_T *lval_root_save = lval_root; - int res; #ifdef LOG_LOCKVAR ch_log(NULL, "LKVAR: execute INS_LOCKUNLOCK isn_arg %s", iptr->isn_arg.string); #endif + lval_root_T *lval_root_save = lval_root; // Stack has the local variable, argument the whole :lock // or :unlock command, like ISN_EXEC. --ectx->ec_stack.ga_len; - lval_root_T root = { STACK_TV_BOT(0), - iptr->isn_arg.lockunlock.lu_cl_exec, - iptr->isn_arg.lockunlock.lu_is_arg }; + lval_root_T root = { .lr_tv = STACK_TV_BOT(0), + .lr_cl_exec = iptr->isn_arg.lockunlock.lu_cl_exec, + .lr_is_arg = iptr->isn_arg.lockunlock.lu_is_arg }; lval_root = &root; - res = exec_command(iptr, + int res = exec_command(iptr, iptr->isn_arg.lockunlock.lu_string); clear_tv(root.lr_tv); lval_root = lval_root_save; From 1ea428883f16838aca5763aee156fde3929d9ab6 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Wed, 11 Oct 2023 21:43:52 +0200 Subject: [PATCH 27/47] patch 9.0.2016: Vim9: assignment operators don't work for class vars Problem: Vim9: assignment operators don't work for class vars Solution: implement it closes: #13306 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan --- src/proto/vim9class.pro | 4 +-- src/testdir/test_vim9_class.vim | 46 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9class.c | 4 +-- src/vim9compile.c | 20 ++++++++++++-- 5 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro index c9e19d0067..362e2cac5e 100644 --- a/src/proto/vim9class.pro +++ b/src/proto/vim9class.pro @@ -1,8 +1,8 @@ /* vim9class.c */ int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl); void ex_class(exarg_T *eap); -type_T *class_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx); -type_T *class_member_type_by_idx(class_T *cl, int is_object, int member_idx); +type_T *oc_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx); +type_T *oc_member_type_by_idx(class_T *cl, int is_object, int member_idx); void ex_enum(exarg_T *eap); void ex_type(exarg_T *eap); int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose); diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index eead19283a..1aa9de7b99 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -7431,4 +7431,50 @@ def Test_funcref_argtype_returntype_check() v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected func(object): object but got func(object): object', 1) enddef +" Test for using an operator (e.g. +) with an assignment +def Test_op_and_assignment() + # Using += with a class variable + var lines =<< trim END + vim9script + class A + public static val: list = [] + static def Foo(): list + val += [1] + return val + enddef + endclass + def Bar(): list + A.val += [2] + return A.val + enddef + assert_equal([1], A.Foo()) + assert_equal([1, 2], Bar()) + A.val += [3] + assert_equal([1, 2, 3], A.val) + END + v9.CheckSourceSuccess(lines) + + # Using += with an object variable + lines =<< trim END + vim9script + class A + public this.val: list = [] + def Foo(): list + this.val += [1] + return this.val + enddef + endclass + def Bar(bar_a: A): list + bar_a.val += [2] + return bar_a.val + enddef + var a = A.new() + assert_equal([1], a.Foo()) + assert_equal([1, 2], Bar(a)) + a.val += [3] + assert_equal([1, 2, 3], a.val) + END + v9.CheckSourceSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 61f6289e28..4031debd86 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2016, /**/ 2015, /**/ diff --git a/src/vim9class.c b/src/vim9class.c index 0cb353bb57..5dda700fa3 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -2032,7 +2032,7 @@ ex_class(exarg_T *eap) * Set *p_m ocmmember_T if not NULL */ type_T * -class_member_type( +oc_member_type( class_T *cl, int is_object, char_u *name, @@ -2060,7 +2060,7 @@ class_member_type( * Given a class or object variable index, return the variable type */ type_T * -class_member_type_by_idx( +oc_member_type_by_idx( class_T *cl, int is_object, int member_idx) diff --git a/src/vim9compile.c b/src/vim9compile.c index 7e1914b694..544ad17e10 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1770,7 +1770,7 @@ compile_lhs( lhs->lhs_dest = dest_class_member; lhs->lhs_class = cctx->ctx_ufunc->uf_class; lhs->lhs_type = - class_member_type_by_idx(cctx->ctx_ufunc->uf_class, + oc_member_type_by_idx(cctx->ctx_ufunc->uf_class, FALSE, lhs->lhs_classmember_idx); } else @@ -2254,7 +2254,7 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u *var_start, cctx_T *cctx) return FAIL; class_T *cl = lhs->lhs_type->tt_class; - type_T *type = class_member_type(cl, TRUE, dot + 1, + type_T *type = oc_member_type(cl, TRUE, dot + 1, lhs->lhs_end, &lhs->lhs_member_idx); if (lhs->lhs_member_idx < 0) return FAIL; @@ -2275,6 +2275,22 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u *var_start, cctx_T *cctx) return generate_GET_ITF_MEMBER(cctx, cl, lhs->lhs_member_idx, type); return generate_GET_OBJ_MEMBER(cctx, lhs->lhs_member_idx, type); } + else if (lhs->lhs_type->tt_type == VAR_CLASS) + { + // ".value": load class variable "classname.value" + char_u *dot = vim_strchr(var_start, '.'); + if (dot == NULL) + return FAIL; + + class_T *cl = lhs->lhs_type->tt_class; + ocmember_T *m = class_member_lookup(cl, dot + 1, + lhs->lhs_end - dot - 1, + &lhs->lhs_member_idx); + if (m == NULL) + return FAIL; + + return generate_CLASSMEMBER(cctx, TRUE, cl, lhs->lhs_member_idx); + } compile_load_lhs(lhs, var_start, NULL, cctx); From dd75fcfbdff1934c6e531b5a89ebc636318bf4a2 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 11 Oct 2023 21:51:19 +0200 Subject: [PATCH 28/47] patch 9.0.2017: linebreak applies for leading whitespace Problem: linebreak applies for leading whitespace Solution: only apply linebreak, once we have found non-breakat chars in the line closes: #13228 closes: #13243 Signed-off-by: Christian Brabandt --- src/charset.c | 10 +++++++++- src/drawline.c | 24 +++++++++++++++++++++++- src/testdir/test_listlbr.vim | 15 +++++++++++++++ src/version.c | 2 ++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/charset.c b/src/charset.c index dc8fa44833..50a81cccd9 100644 --- a/src/charset.c +++ b/src/charset.c @@ -1123,6 +1123,7 @@ win_lbr_chartabsize( int n; char_u *sbr; int no_sbr = FALSE; + colnr_T vcol_start = 0; // start from where to consider linebreak #endif #if defined(FEAT_PROP_POPUP) @@ -1344,7 +1345,14 @@ win_lbr_chartabsize( * If 'linebreak' set check at a blank before a non-blank if the line * needs a break here */ - if (wp->w_p_lbr + if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width != 0) + { + char_u *t = cts->cts_line; + while (VIM_ISBREAK((int)*t)) + t++; + vcol_start = t - cts->cts_line; + } + if (wp->w_p_lbr && vcol_start <= vcol && VIM_ISBREAK((int)s[0]) && !VIM_ISBREAK((int)s[1]) && wp->w_p_wrap diff --git a/src/drawline.c b/src/drawline.c index 4d63fae34d..309fcd4469 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -171,6 +171,11 @@ typedef struct { #ifdef FEAT_SIGNS sign_attrs_T sattr; #endif +#ifdef FEAT_LINEBREAK + // do consider wrapping in linebreak mode only after encountering + // a non whitespace char + int need_lbr; +#endif } winlinevars_T; // draw_state values for items that are drawn in sequence: @@ -968,6 +973,9 @@ win_line_start(win_T *wp UNUSED, winlinevars_T *wlv, int save_extra) { wlv->col = 0; wlv->off = (unsigned)(current_ScreenLine - ScreenLines); +#ifdef FEAT_LINEBREAK + wlv->need_lbr = FALSE; +#endif #ifdef FEAT_RIGHTLEFT if (wp->w_p_rl) @@ -994,6 +1002,9 @@ win_line_start(win_T *wp UNUSED, winlinevars_T *wlv, int save_extra) wlv->saved_extra_for_textprop = wlv->extra_for_textprop; wlv->saved_c_extra = wlv->c_extra; wlv->saved_c_final = wlv->c_final; +#ifdef FEAT_LINEBREAK + wlv->need_lbr = TRUE; +#endif #ifdef FEAT_SYN_HL if (!(wlv->cul_screenline # ifdef FEAT_DIFF @@ -2904,9 +2915,20 @@ win_line( wlv.char_attr); } #endif +#ifdef FEAT_LINEBREAK + // we don't want linebreak to apply for lines that start with + // leading spaces, followed by long letters (since it would add + // a break at the beginning of a line and this might be unexpected) + // + // So only allow to linebreak, once we have found chars not in + // 'breakat' in the line. + if ( wp->w_p_lbr && !wlv.need_lbr && c != NUL && + !VIM_ISBREAK((int)*ptr)) + wlv.need_lbr = TRUE; +#endif #ifdef FEAT_LINEBREAK // Found last space before word: check for line break. - if (wp->w_p_lbr && c0 == c + if (wp->w_p_lbr && c0 == c && wlv.need_lbr && VIM_ISBREAK(c) && !VIM_ISBREAK((int)*ptr)) { int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) diff --git a/src/testdir/test_listlbr.vim b/src/testdir/test_listlbr.vim index e583e4831a..bcbd886abe 100644 --- a/src/testdir/test_listlbr.vim +++ b/src/testdir/test_listlbr.vim @@ -372,4 +372,19 @@ func Test_ctrl_char_on_wrap_column() call s:close_windows() endfunc +func Test_linebreak_no_break_after_whitespace_only() + call s:test_windows('setl ts=4 linebreak wrap') + call setline(1, "\tabcdefghijklmnopqrstuvwxyz" .. + \ "abcdefghijklmnopqrstuvwxyz") + let lines = s:screen_lines([1, 4], winwidth(0)) + let expect = [ +\ " abcdefghijklmnop", +\ "qrstuvwxyzabcdefghij", +\ "klmnopqrstuvwxyz ", +\ "~ ", +\ ] + call s:compare_lines(expect, lines) + call s:close_windows() +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 4031debd86..7db93c42b3 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2017, /**/ 2016, /**/ From 69fb5afb3bc9da24c2fb0eafb0027ba9c6502fc2 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Wed, 11 Oct 2023 21:55:56 +0200 Subject: [PATCH 29/47] patch 9.0.2018: complete_info() returns wrong index Problem: complete_info() returns wrong index Solution: Make order of 'info' in completion_info consistent Start the iteration from the same point and follow the same direction as done when assigning the completion numbers. This way we remove the dependence on the completion direction and make the order of 'info' consistent. closes: #12230 closes: #12971 Signed-off-by: Christian Brabandt Co-authored-by: LemonBoy --- src/insexpand.c | 69 +++++++++++++++++-------------- src/testdir/test_ins_complete.vim | 38 +++++++++++++++++ src/version.c | 2 + 3 files changed, 79 insertions(+), 30 deletions(-) diff --git a/src/insexpand.c b/src/insexpand.c index 3b06ee02de..f225cd3844 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -3040,6 +3040,43 @@ ins_compl_update_sequence_numbers(void) } } + static int +info_add_completion_info(list_T *li) +{ + compl_T *match; + + if (compl_first_match == NULL) + return OK; + + // Skip the element with the CP_ORIGINAL_TEXT flag at the beginning, in case of + // forward completion, or at the end, in case of backward completion. + match = compl_dir_forward() + ? compl_first_match->cp_next : compl_first_match->cp_prev->cp_prev; + while (match != NULL && !match_at_original_text(match)) + { + dict_T *di = dict_alloc(); + + if (di == NULL) + return FAIL; + if (list_append_dict(li, di) == FAIL) + return FAIL; + dict_add_string(di, "word", match->cp_str); + dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]); + dict_add_string(di, "menu", match->cp_text[CPT_MENU]); + dict_add_string(di, "kind", match->cp_text[CPT_KIND]); + dict_add_string(di, "info", match->cp_text[CPT_INFO]); + if (match->cp_user_data.v_type == VAR_UNKNOWN) + // Add an empty string for backwards compatibility + dict_add_string(di, "user_data", (char_u *)""); + else + dict_add_tv(di, "user_data", &match->cp_user_data); + + match = compl_dir_forward() ? match->cp_next : match->cp_prev; + } + + return OK; +} + /* * Get complete information */ @@ -3088,41 +3125,13 @@ get_complete_info(list_T *what_list, dict_T *retdict) if (ret == OK && (what_flag & CI_WHAT_ITEMS)) { list_T *li; - dict_T *di; - compl_T *match; li = list_alloc(); if (li == NULL) return; ret = dict_add_list(retdict, "items", li); - if (ret == OK && compl_first_match != NULL) - { - match = compl_first_match; - do - { - if (!match_at_original_text(match)) - { - di = dict_alloc(); - if (di == NULL) - return; - ret = list_append_dict(li, di); - if (ret != OK) - return; - dict_add_string(di, "word", match->cp_str); - dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]); - dict_add_string(di, "menu", match->cp_text[CPT_MENU]); - dict_add_string(di, "kind", match->cp_text[CPT_KIND]); - dict_add_string(di, "info", match->cp_text[CPT_INFO]); - if (match->cp_user_data.v_type == VAR_UNKNOWN) - // Add an empty string for backwards compatibility - dict_add_string(di, "user_data", (char_u *)""); - else - dict_add_tv(di, "user_data", &match->cp_user_data); - } - match = match->cp_next; - } - while (match != NULL && !is_first_match(match)); - } + if (ret == OK) + ret = info_add_completion_info(li); } if (ret == OK && (what_flag & CI_WHAT_SELECTED)) diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index cedc09f7ad..c7f9e9b28b 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -2243,5 +2243,43 @@ func Test_ins_complete_popup_position() call StopVimInTerminal(buf) endfunc +func GetCompleteInfo() + let g:compl_info = complete_info() + return '' +endfunc + +func Test_complete_info_index() + new + call setline(1, ["aaa", "bbb", "ccc", "ddd", "eee", "fff"]) + inoremap =GetCompleteInfo() + + " Ensure 'index' in complete_info() is coherent with the 'items' array. + + set completeopt=menu,preview + " Search forward. + call feedkeys("Go\\\\_dd", 'tx') + call assert_equal("aaa", g:compl_info['items'][g:compl_info['selected']]['word']) + call feedkeys("Go\\\\\_dd", 'tx') + call assert_equal("bbb", g:compl_info['items'][g:compl_info['selected']]['word']) + + " Search backward. + call feedkeys("Go\\\\_dd", 'tx') + call assert_equal("fff", g:compl_info['items'][g:compl_info['selected']]['word']) + call feedkeys("Go\\\\\_dd", 'tx') + call assert_equal("eee", g:compl_info['items'][g:compl_info['selected']]['word']) + + " Add 'noselect', check that 'selected' is -1 when nothing is selected. + set completeopt+=noselect + " Search forward. + call feedkeys("Go\\\\_dd", 'tx') + call assert_equal(-1, g:compl_info['selected']) + + " Search backward. + call feedkeys("Go\\\\_dd", 'tx') + call assert_equal(-1, g:compl_info['selected']) + + set completeopt& + bwipe! +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 7db93c42b3..d05016389a 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2018, /**/ 2017, /**/ From 29bb67f1beefc7fd393dbfd9ee77d92f1db3a3c0 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sat, 14 Oct 2023 11:18:50 +0200 Subject: [PATCH 30/47] patch 9.0.2019: Vim9: no support for funcrefs Problem: Vim9: no support for funcrefs Solution: Add support for object/class funcref members closes: #11981 #12417 #12960 #12324 #13333 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan --- src/eval.c | 11 + src/evalfunc.c | 3 + src/proto/vim9class.pro | 3 +- src/proto/vim9instr.pro | 2 +- src/structs.h | 1 + src/testdir/test_vim9_class.vim | 476 ++++++++++++++++++++++++++++++++ src/testdir/test_vim9_func.vim | 11 + src/version.c | 2 + src/vim9.h | 1 + src/vim9class.c | 115 ++++++-- src/vim9compile.c | 2 +- src/vim9execute.c | 69 ++++- src/vim9expr.c | 89 +++++- src/vim9instr.c | 2 + src/vim9type.c | 4 +- 15 files changed, 741 insertions(+), 50 deletions(-) diff --git a/src/eval.c b/src/eval.c index 4da6246ace..80ff5a3cba 100644 --- a/src/eval.c +++ b/src/eval.c @@ -5256,6 +5256,7 @@ partial_free(partial_T *pt) } else func_ptr_unref(pt->pt_func); + object_unref(pt->pt_obj); // "out_up" is no longer used, decrement refcount on partial that owns it. partial_unref(pt->pt_outer.out_up_partial); @@ -5578,6 +5579,7 @@ free_unref_items(int copyID) /* * PASS 2: free the items themselves. */ + object_free_items(copyID); dict_free_items(copyID); list_free_items(copyID); @@ -5818,6 +5820,15 @@ set_ref_in_item_partial( set_ref_in_item(&dtv, copyID, ht_stack, list_stack); } + if (pt->pt_obj != NULL) + { + typval_T objtv; + + objtv.v_type = VAR_OBJECT; + objtv.vval.v_object = pt->pt_obj; + set_ref_in_item(&objtv, copyID, ht_stack, list_stack); + } + for (int i = 0; i < pt->pt_argc; ++i) abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID, ht_stack, list_stack); diff --git a/src/evalfunc.c b/src/evalfunc.c index 5fccf5270c..85c64a23a2 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -4801,6 +4801,9 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref) pt->pt_auto = arg_pt->pt_auto; if (pt->pt_dict != NULL) ++pt->pt_dict->dv_refcount; + pt->pt_obj = arg_pt->pt_obj; + if (pt->pt_obj != NULL) + ++pt->pt_obj->obj_refcount; } pt->pt_refcount = 1; diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro index 362e2cac5e..62d1b7d0f3 100644 --- a/src/proto/vim9class.pro +++ b/src/proto/vim9class.pro @@ -17,13 +17,14 @@ void emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl); ufunc_T *method_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx); int inside_class(cctx_T *cctx_arg, class_T *cl); void copy_object(typval_T *from, typval_T *to); -void object_unref(object_T *obj); void copy_class(typval_T *from, typval_T *to); void class_unref(class_T *cl); int class_free_nonref(int copyID); int set_ref_in_classes(int copyID); void object_created(object_T *obj); +void object_unref(object_T *obj); int object_free_nonref(int copyID); +void object_free_items(int copyID); void method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len); void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len); void f_instanceof(typval_T *argvars, typval_T *rettv); diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro index 898eb9751c..a236b75612 100644 --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -45,7 +45,7 @@ int generate_OLDSCRIPT(cctx_T *cctx, isntype_T isn_type, char_u *name, int sid, int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type_T *type); int generate_NEWLIST(cctx_T *cctx, int count, int use_null); int generate_NEWDICT(cctx_T *cctx, int count, int use_null); -int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int fi, int *isn_idx); +int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int object_method, int fi, int *isn_idx); int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name); int generate_DEF(cctx_T *cctx, char_u *name, size_t len); int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where); diff --git a/src/structs.h b/src/structs.h index 5131858741..a221a0406d 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2316,6 +2316,7 @@ struct partial_S int pt_copyID; // funcstack may contain pointer to partial dict_T *pt_dict; // dict for "self" + object_T *pt_obj; // object method }; typedef struct { diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 1aa9de7b99..3af9a1f87a 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -7477,4 +7477,480 @@ def Test_op_and_assignment() v9.CheckSourceSuccess(lines) enddef +" Test for using an object method as a funcref +def Test_object_funcref() + # Using object method funcref from a def function + var lines =<< trim END + vim9script + class A + def Foo(): list + return [3, 2, 1] + enddef + endclass + def Bar() + var a = A.new() + var Fn = a.Foo + assert_equal([3, 2, 1], Fn()) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using object method funcref at the script level + lines =<< trim END + vim9script + class A + def Foo(): dict + return {a: 1, b: 2} + enddef + endclass + var a = A.new() + var Fn = a.Foo + assert_equal({a: 1, b: 2}, Fn()) + END + v9.CheckSourceSuccess(lines) + + # Using object method funcref from another object method + lines =<< trim END + vim9script + class A + def Foo(): list + return [3, 2, 1] + enddef + def Bar() + var Fn = this.Foo + assert_equal([3, 2, 1], Fn()) + enddef + endclass + var a = A.new() + a.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using function() to get a object method funcref + lines =<< trim END + vim9script + class A + def Foo(l: list): list + return l + enddef + endclass + var a = A.new() + var Fn = function(a.Foo, [[{a: 1, b: 2}, [3, 4]]]) + assert_equal([{a: 1, b: 2}, [3, 4]], Fn()) + END + v9.CheckSourceSuccess(lines) + + # Use an object method with a function returning a funcref and then call the + # funcref. + lines =<< trim END + vim9script + + def Map(F: func(number): number): func(number): number + return (n: number) => F(n) + enddef + + class Math + def Double(n: number): number + return 2 * n + enddef + endclass + + const math = Math.new() + assert_equal(48, Map(math.Double)(24)) + END + v9.CheckSourceSuccess(lines) + + # Try using a private object method funcref from a def function + lines =<< trim END + vim9script + class A + def _Foo() + enddef + endclass + def Bar() + var a = A.new() + var Fn = a._Foo + enddef + Bar() + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 2) + + # Try using a private object method funcref at the script level + lines =<< trim END + vim9script + class A + def _Foo() + enddef + endclass + var a = A.new() + var Fn = a._Foo + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 7) + + # Using a private object method funcref from another object method + lines =<< trim END + vim9script + class A + def _Foo(): list + return [3, 2, 1] + enddef + def Bar() + var Fn = this._Foo + assert_equal([3, 2, 1], Fn()) + enddef + endclass + var a = A.new() + a.Bar() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using a class method as a funcref +def Test_class_funcref() + # Using class method funcref in a def function + var lines =<< trim END + vim9script + class A + static def Foo(): list + return [3, 2, 1] + enddef + endclass + def Bar() + var Fn = A.Foo + assert_equal([3, 2, 1], Fn()) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using class method funcref at script level + lines =<< trim END + vim9script + class A + static def Foo(): dict + return {a: 1, b: 2} + enddef + endclass + var Fn = A.Foo + assert_equal({a: 1, b: 2}, Fn()) + END + v9.CheckSourceSuccess(lines) + + # Using function() to get a class method funcref + lines =<< trim END + vim9script + class A + static def Foo(l: list): list + return l + enddef + endclass + var Fn = function(A.Foo, [[{a: 1, b: 2}, [3, 4]]]) + assert_equal([{a: 1, b: 2}, [3, 4]], Fn()) + END + v9.CheckSourceSuccess(lines) + + # Using a class method funcref from another class method + lines =<< trim END + vim9script + class A + static def Foo(): list + return [3, 2, 1] + enddef + static def Bar() + var Fn = Foo + assert_equal([3, 2, 1], Fn()) + enddef + endclass + A.Bar() + END + v9.CheckSourceSuccess(lines) + + # Use a class method with a function returning a funcref and then call the + # funcref. + lines =<< trim END + vim9script + + def Map(F: func(number): number): func(number): number + return (n: number) => F(n) + enddef + + class Math + static def StaticDouble(n: number): number + return 2 * n + enddef + endclass + + assert_equal(48, Map(Math.StaticDouble)(24)) + END + v9.CheckSourceSuccess(lines) + + # Try using a private class method funcref in a def function + lines =<< trim END + vim9script + class A + static def _Foo() + enddef + endclass + def Bar() + var Fn = A._Foo + enddef + Bar() + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 1) + + # Try using a private class method funcref at script level + lines =<< trim END + vim9script + class A + static def _Foo() + enddef + endclass + var Fn = A._Foo + END + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 6) + + # Using a private class method funcref from another class method + lines =<< trim END + vim9script + class A + static def _Foo(): list + return [3, 2, 1] + enddef + static def Bar() + var Fn = _Foo + assert_equal([3, 2, 1], Fn()) + enddef + endclass + A.Bar() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using an object member as a funcref +def Test_object_member_funcref() + # Using a funcref object variable in an object method + var lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + this.Cb: func(number): number = Foo + def Bar() + assert_equal(200, this.Cb(20)) + enddef + endclass + + var a = A.new() + a.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref object variable in a def method + lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + this.Cb: func(number): number = Foo + endclass + + def Bar() + var a = A.new() + assert_equal(200, a.Cb(20)) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref object variable at script level + lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + this.Cb: func(number): number = Foo + endclass + + var a = A.new() + assert_equal(200, a.Cb(20)) + END + v9.CheckSourceSuccess(lines) + + # Using a funcref object variable pointing to an object method in an object + # method. + lines =<< trim END + vim9script + class A + this.Cb: func(number): number = this.Foo + def Foo(n: number): number + return n * 10 + enddef + def Bar() + assert_equal(200, this.Cb(20)) + enddef + endclass + + var a = A.new() + a.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref object variable pointing to an object method in a def + # method. + lines =<< trim END + vim9script + class A + this.Cb: func(number): number = this.Foo + def Foo(n: number): number + return n * 10 + enddef + endclass + + def Bar() + var a = A.new() + assert_equal(200, a.Cb(20)) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref object variable pointing to an object method at script + # level. + lines =<< trim END + vim9script + class A + this.Cb = this.Foo + def Foo(n: number): number + return n * 10 + enddef + endclass + + var a = A.new() + assert_equal(200, a.Cb(20)) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for using a class member as a funcref +def Test_class_member_funcref() + # Using a funcref class variable in a class method + var lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + static Cb = Foo + static def Bar() + assert_equal(200, Cb(20)) + enddef + endclass + + A.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref class variable in a def method + lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + public static Cb = Foo + endclass + + def Bar() + assert_equal(200, A.Cb(20)) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref class variable at script level + lines =<< trim END + vim9script + def Foo(n: number): number + return n * 10 + enddef + + class A + public static Cb = Foo + endclass + + assert_equal(200, A.Cb(20)) + END + v9.CheckSourceSuccess(lines) + + # Using a funcref class variable pointing to a class method in a class + # method. + lines =<< trim END + vim9script + class A + static Cb: func(number): number + static def Foo(n: number): number + return n * 10 + enddef + static def Init() + Cb = Foo + enddef + static def Bar() + assert_equal(200, Cb(20)) + enddef + endclass + + A.Init() + A.Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref class variable pointing to a class method in a def method. + lines =<< trim END + vim9script + class A + static Cb: func(number): number + static def Foo(n: number): number + return n * 10 + enddef + static def Init() + Cb = Foo + enddef + endclass + + def Bar() + A.Init() + assert_equal(200, A.Cb(20)) + enddef + Bar() + END + v9.CheckSourceSuccess(lines) + + # Using a funcref class variable pointing to a class method at script level. + lines =<< trim END + vim9script + class A + static Cb: func(number): number + static def Foo(n: number): number + return n * 10 + enddef + static def Init() + Cb = Foo + enddef + endclass + + A.Init() + assert_equal(200, A.Cb(20)) + END + v9.CheckSourceSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 852f3a7ede..597a10c581 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -3663,6 +3663,17 @@ def Test_partial_call() const Call = Foo(Expr) END v9.CheckScriptFailure(lines, 'E1031:') + + # Test for calling a partial that takes a single argument. + # This used to produce a "E340: Internal error" message. + lines =<< trim END + def Foo(n: number): number + return n * 2 + enddef + var Fn = function(Foo, [10]) + assert_equal(20, Fn()) + END + v9.CheckDefAndScriptSuccess(lines) enddef def Test_partial_double_nested() diff --git a/src/version.c b/src/version.c index d05016389a..9147441a40 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2019, /**/ 2018, /**/ diff --git a/src/vim9.h b/src/vim9.h index 785486963a..6bfbd9ee8f 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -383,6 +383,7 @@ typedef struct { char_u *fre_func_name; // function name for legacy function loopvarinfo_T fre_loopvar_info; // info about variables inside loops class_T *fre_class; // class for a method + int fre_object_method; // class or object method int fre_method_idx; // method index on "fre_class" } funcref_extra_T; diff --git a/src/vim9class.c b/src/vim9class.c index 5dda700fa3..bfa6149602 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -2167,15 +2167,35 @@ call_oc_method( ufunc_T *fp; typval_T argvars[MAX_FUNC_ARGS + 1]; int argcount = 0; + ocmember_T *ocm = NULL; + int m_idx; fp = method_lookup(cl, rettv->v_type, name, len, NULL); if (fp == NULL) { - method_not_found_msg(cl, rettv->v_type, name, len); - return FAIL; + // could be an object or class funcref variable + ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx); + if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC) + { + method_not_found_msg(cl, rettv->v_type, name, len); + return FAIL; + } + + if (rettv->v_type == VAR_OBJECT) + { + // funcref object variable + object_T *obj = rettv->vval.v_object; + typval_T *tv = (typval_T *)(obj + 1) + m_idx; + copy_tv(tv, rettv); + } + else + // funcref class variable + copy_tv(&cl->class_members_tv[m_idx], rettv); + *arg = name_end; + return OK; } - if (*fp->uf_name == '_') + if (ocm == NULL && *fp->uf_name == '_') { // Cannot access a private method outside of a class semsg(_(e_cannot_access_private_method_str), fp->uf_name); @@ -2288,6 +2308,37 @@ class_object_index( return OK; } + // could be a class method or an object method + int fidx; + ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx); + if (fp != NULL) + { + // Private methods are not accessible outside the class + if (*name == '_') + { + semsg(_(e_cannot_access_private_method_str), fp->uf_name); + return FAIL; + } + + partial_T *pt = ALLOC_CLEAR_ONE(partial_T); + if (pt == NULL) + return FAIL; + + pt->pt_refcount = 1; + if (is_object) + { + pt->pt_obj = rettv->vval.v_object; + ++pt->pt_obj->obj_refcount; + } + pt->pt_auto = TRUE; + pt->pt_func = fp; + func_ptr_ref(pt->pt_func); + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; + *arg = name_end; + return OK; + } + if (did_emsg == did_emsg_save) member_not_found_msg(cl, is_object, name, len); } @@ -2774,8 +2825,6 @@ object_created(object_T *obj) first_object = obj; } -static object_T *next_nonref_obj = NULL; - /* * Call this function when an object has been cleared and is about to be freed. * It is removed from the list headed by "first_object". @@ -2789,30 +2838,35 @@ object_cleared(object_T *obj) obj->obj_prev_used->obj_next_used = obj->obj_next_used; else if (first_object == obj) first_object = obj->obj_next_used; - - // update the next object to check if needed - if (obj == next_nonref_obj) - next_nonref_obj = obj->obj_next_used; } /* - * Free an object. + * Free the contents of an object ignoring the reference count. */ static void -object_clear(object_T *obj) +object_free_contents(object_T *obj) { - // Avoid a recursive call, it can happen if "obj" has a circular reference. - obj->obj_refcount = INT_MAX; - class_T *cl = obj->obj_class; if (!cl) return; + // Avoid a recursive call, it can happen if "obj" has a circular reference. + obj->obj_refcount = INT_MAX; + // the member values are just after the object structure typval_T *tv = (typval_T *)(obj + 1); for (int i = 0; i < cl->class_obj_member_count; ++i) clear_tv(tv + i); +} + + static void +object_free_object(object_T *obj) +{ + class_T *cl = obj->obj_class; + + if (!cl) + return; // Remove from the list headed by "first_object". object_cleared(obj); @@ -2821,6 +2875,16 @@ object_clear(object_T *obj) class_unref(cl); } + static void +object_free(object_T *obj) +{ + if (in_free_unref_items) + return; + + object_free_contents(obj); + object_free_object(obj); +} + /* * Unreference an object. */ @@ -2828,7 +2892,7 @@ object_clear(object_T *obj) object_unref(object_T *obj) { if (obj != NULL && --obj->obj_refcount <= 0) - object_clear(obj); + object_free(obj); } /* @@ -2839,21 +2903,32 @@ object_free_nonref(int copyID) { int did_free = FALSE; - for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj) + for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used) { - next_nonref_obj = obj->obj_next_used; if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) { - // Free the object and items it contains. - object_clear(obj); + // Free the object contents. Object itself will be freed later. + object_free_contents(obj); did_free = TRUE; } } - next_nonref_obj = NULL; return did_free; } + void +object_free_items(int copyID) +{ + object_T *obj_next; + + for (object_T *obj = first_object; obj != NULL; obj = obj_next) + { + obj_next = obj->obj_next_used; + if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) + object_free_object(obj); + } +} + /* * Output message which takes a variable name and the class that defines it. * "cl" is that class where the name was found. Search "cl"'s hierarchy to diff --git a/src/vim9compile.c b/src/vim9compile.c index 544ad17e10..03e79f5655 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1148,7 +1148,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free) ASSIGN_CONST, ufunc->uf_func_type); if (lvar == NULL) goto theend; - if (generate_FUNCREF(cctx, ufunc, NULL, 0, &funcref_isn_idx) == FAIL) + if (generate_FUNCREF(cctx, ufunc, NULL, FALSE, 0, &funcref_isn_idx) == FAIL) goto theend; r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); } diff --git a/src/vim9execute.c b/src/vim9execute.c index a89bcd19b4..d8087bf08d 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -616,6 +616,12 @@ call_dfunc( // the first local variable. if (IS_OBJECT_METHOD(ufunc)) { + if (obj->v_type != VAR_OBJECT) + { + semsg(_(e_internal_error_str), "type in stack is not an object"); + return FAIL; + } + *STACK_TV_VAR(0) = *obj; obj->v_type = VAR_UNKNOWN; } @@ -1497,6 +1503,23 @@ call_partial( partial_T *pt = tv->vval.v_partial; int i; + if (pt->pt_obj != NULL) + { + // partial with an object method. Push the object before the + // function arguments. + if (GA_GROW_FAILS(&ectx->ec_stack, 1)) + return FAIL; + for (i = 1; i <= argcount; ++i) + *STACK_TV_BOT(-i + 1) = *STACK_TV_BOT(-i); + + typval_T *obj_tv = STACK_TV_BOT(-argcount); + obj_tv->v_type = VAR_OBJECT; + obj_tv->v_lock = 0; + obj_tv->vval.v_object = pt->pt_obj; + ++pt->pt_obj->obj_refcount; + ++ectx->ec_stack.ga_len; + } + if (pt->pt_argc > 0) { // Make space for arguments from the partial, shift the "argcount" @@ -4447,20 +4470,44 @@ exec_instructions(ectx_T *ectx) } if (extra != NULL && extra->fre_class != NULL) { - tv = STACK_TV_BOT(-1); - if (tv->v_type != VAR_OBJECT) + class_T *cl; + if (extra->fre_object_method) { - object_required_error(tv); - vim_free(pt); - goto on_error; + tv = STACK_TV_BOT(-1); + if (tv->v_type != VAR_OBJECT) + { + object_required_error(tv); + vim_free(pt); + goto on_error; + } + + object_T *obj = tv->vval.v_object; + cl = obj->obj_class; + // drop the value from the stack + clear_tv(tv); + --ectx->ec_stack.ga_len; + + pt->pt_obj = obj; + ++obj->obj_refcount; } - object_T *obj = tv->vval.v_object; - class_T *cl = obj->obj_class; + else + cl = extra->fre_class; - // convert the interface index to the object index - int idx = object_index_from_itf_index(extra->fre_class, - TRUE, extra->fre_method_idx, cl); - ufunc = cl->class_obj_methods[idx]; + if (extra->fre_object_method) + { + // object method + // convert the interface index to the object index + int idx = + object_index_from_itf_index(extra->fre_class, + TRUE, extra->fre_method_idx, cl); + ufunc = cl->class_obj_methods[idx]; + } + else + { + // class method + ufunc = + cl->class_class_functions[extra->fre_method_idx]; + } } else if (extra == NULL || extra->fre_func_name == NULL) { diff --git a/src/vim9expr.c b/src/vim9expr.c index c15021e82a..c91ca9325b 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -281,6 +281,8 @@ inside_class_hierarchy(cctx_T *cctx_arg, class_T *cl) static int compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) { + int m_idx; + if (VIM_ISWHITE((*arg)[1])) { semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg); @@ -365,17 +367,34 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) break; } } + ocmember_T *ocm = NULL; if (ufunc == NULL) { - method_not_found_msg(cl, type->tt_type, name, len); - return FAIL; + // could be a funcref in a member variable + ocm = member_lookup(cl, type->tt_type, name, len, &m_idx); + if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC) + { + method_not_found_msg(cl, type->tt_type, name, len); + return FAIL; + } + if (type->tt_type == VAR_CLASS) + { + if (generate_CLASSMEMBER(cctx, TRUE, cl, m_idx) == FAIL) + return FAIL; + } + else + { + if (generate_GET_OBJ_MEMBER(cctx, m_idx, ocm->ocm_type) == + FAIL) + return FAIL; + } } // A private object method can be used only inside the class where it // is defined or in one of the child classes. // A private class method can be used only in the class where it is // defined. - if (*ufunc->uf_name == '_' && + if (ocm == NULL && *ufunc->uf_name == '_' && ((type->tt_type == VAR_OBJECT && !inside_class_hierarchy(cctx, cl)) || (type->tt_type == VAR_CLASS @@ -393,6 +412,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL) return FAIL; + if (ocm != NULL) + return generate_PCALL(cctx, argcount, name, ocm->ocm_type, TRUE); if (type->tt_type == VAR_OBJECT && (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED))) return generate_CALL(cctx, ufunc, cl, fi, argcount); @@ -401,7 +422,6 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) if (type->tt_type == VAR_OBJECT) { - int m_idx; ocmember_T *m = object_member_lookup(cl, name, len, &m_idx); if (m_idx >= 0) { @@ -418,15 +438,21 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) return generate_GET_OBJ_MEMBER(cctx, m_idx, m->ocm_type); } - // Could be a function reference: "obj.Func". + // Could be an object method reference: "obj.Func". m_idx = object_method_idx(cl, name, len); if (m_idx >= 0) { ufunc_T *fp = cl->class_obj_methods[m_idx]; - if (type->tt_type == VAR_OBJECT - && (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED))) - return generate_FUNCREF(cctx, fp, cl, m_idx, NULL); - return generate_FUNCREF(cctx, fp, NULL, 0, NULL); + // Private methods are not accessible outside the class + if (*name == '_' && !inside_class(cctx, cl)) + { + semsg(_(e_cannot_access_private_method_str), fp->uf_name); + return FAIL; + } + *arg = name_end; + if (type->tt_type == VAR_OBJECT) + return generate_FUNCREF(cctx, fp, cl, TRUE, m_idx, NULL); + return generate_FUNCREF(cctx, fp, NULL, FALSE, 0, NULL); } member_not_found_msg(cl, VAR_OBJECT, name, len); @@ -451,6 +477,24 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) *arg = name_end; return generate_CLASSMEMBER(cctx, TRUE, cl, idx); } + + // Could be a class method reference: "class.Func". + m_idx = class_method_idx(cl, name, len); + if (m_idx >= 0) + { + ufunc_T *fp = cl->class_class_functions[m_idx]; + // Private methods are not accessible outside the class + if (*name == '_' && !inside_class(cctx, cl)) + { + semsg(_(e_cannot_access_private_method_str), fp->uf_name); + return FAIL; + } + *arg = name_end; + if (type->tt_type == VAR_CLASS) + return generate_FUNCREF(cctx, fp, cl, FALSE, m_idx, NULL); + return generate_FUNCREF(cctx, fp, NULL, FALSE, 0, NULL); + } + member_not_found_msg(cl, VAR_CLASS, name, len); } @@ -716,6 +760,7 @@ compile_load( { size_t len = end - *arg; int idx; + int method_idx; int gen_load = FALSE; int gen_load_outer = 0; int outer_loop_depth = -1; @@ -764,13 +809,27 @@ compile_load( else gen_load = TRUE; } - else if ((idx = cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0) + else if (cctx->ctx_ufunc->uf_defclass != NULL && + (((idx = + cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0) + || ((method_idx = + cctx_class_method_idx(cctx, *arg, len, &cl)) >= 0))) { - // Referencing a class variable without the class name. - // A class variable can be referenced without the class name - // only in the class where the function is defined. + // Referencing a class variable or method without the class + // name. A class variable or method can be referenced without + // the class name only in the class where the function is + // defined. if (cctx->ctx_ufunc->uf_defclass == cl) - res = generate_CLASSMEMBER(cctx, TRUE, cl, idx); + { + if (idx >= 0) + res = generate_CLASSMEMBER(cctx, TRUE, cl, idx); + else + { + ufunc_T *fp = cl->class_class_functions[method_idx]; + res = generate_FUNCREF(cctx, fp, cl, FALSE, method_idx, + NULL); + } + } else { semsg(_(e_class_variable_str_accessible_only_inside_class_str), @@ -1387,7 +1446,7 @@ compile_lambda(char_u **arg, cctx_T *cctx) // The function reference count will be 1. When the ISN_FUNCREF // instruction is deleted the reference count is decremented and the // function is freed. - return generate_FUNCREF(cctx, ufunc, NULL, 0, NULL); + return generate_FUNCREF(cctx, ufunc, NULL, FALSE, 0, NULL); } func_ptr_unref(ufunc); diff --git a/src/vim9instr.c b/src/vim9instr.c index 6de29b617f..f7b074c79a 100644 --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -1384,6 +1384,7 @@ generate_FUNCREF( cctx_T *cctx, ufunc_T *ufunc, class_T *cl, + int object_method, int fi, int *isn_idx) { @@ -1412,6 +1413,7 @@ generate_FUNCREF( { extra->fre_class = cl; ++cl->class_refcount; + extra->fre_object_method = object_method; extra->fre_method_idx = fi; } } diff --git a/src/vim9type.c b/src/vim9type.c index 338aee14a7..00ee76b487 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -144,7 +144,7 @@ alloc_type(type_T *type) if (ret->tt_member != NULL) ret->tt_member = alloc_type(ret->tt_member); - if (type->tt_args != NULL) + if (type->tt_argcount > 0 && type->tt_args != NULL) { int i; @@ -153,6 +153,8 @@ alloc_type(type_T *type) for (i = 0; i < type->tt_argcount; ++i) ret->tt_args[i] = alloc_type(type->tt_args[i]); } + else + ret->tt_args = NULL; return ret; } From 2bbd0d30eebdea66c0da3895e83d999ed6ad83fb Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Sat, 14 Oct 2023 02:23:45 -0700 Subject: [PATCH 31/47] runtime(doc): Improve command-line completion docs (#13331) * Improve command-line completion docs Add more details about 'ignorecase' and its effect on cmdline completion. Make sure keys used in wildmenu are properly documented and linked in the keys' documentation entries, and in `:h index` for proper cross-referencing, as wildmenu popup is slightly different from insert-mode popup menu. * Fix docs typos Signed-off-by: Christian Brabandt --- runtime/doc/cmdline.txt | 25 ++++++++++++++++++------- runtime/doc/index.txt | 16 ++++++++++++++++ runtime/doc/options.txt | 27 +++++++++++++++------------ 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 9493658ed5..6d58d63873 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -90,9 +90,11 @@ CTRL-SHIFT-Q Works just like CTRL-V, unless |modifyOtherKeys| is active, In the GUI the |key-notation| is inserted without simplifying. *c_* *c_Left* - cursor left + cursor left. See 'wildmenu' for behavior during wildmenu + completion mode. *c_* *c_Right* - cursor right + cursor right. See 'wildmenu' for behavior during wildmenu + completion mode. *c_* or *c_* cursor one WORD left @@ -102,7 +104,8 @@ CTRL-SHIFT-Q Works just like CTRL-V, unless |modifyOtherKeys| is active, CTRL-B or *c_CTRL-B* *c_* *c_Home* cursor to beginning of command-line CTRL-E or *c_CTRL-E* *c_* *c_End* - cursor to end of command-line + cursor to end of command-line. See 'wildmenu' for behavior + during wildmenu completion mode. *c_* Move the cursor to the position of the mouse click. @@ -237,6 +240,7 @@ CTRL-\ e {expr} *c_CTRL-\_e* CTRL-Y When there is a modeless selection, copy the selection into the clipboard. |modeless-selection| If there is no selection CTRL-Y is inserted as a character. + See 'wildmenu' for behavior during wildmenu completion mode. CTRL-M or CTRL-J *c_CTRL-M* *c_CTRL-J* *c_* *c_* *c_CR* or start entered command @@ -252,12 +256,14 @@ CTRL-C quit command-line without executing *c_* *c_Up* recall older command-line from history, whose beginning - matches the current command-line (see below). + matches the current command-line (see below). See 'wildmenu' + for behavior during wildmenu completion mode. {not available when compiled without the |+cmdline_hist| feature} *c_* *c_Down* recall more recent command-line from history, whose beginning - matches the current command-line (see below). + matches the current command-line (see below). See 'wildmenu' + for behavior during wildmenu completion mode. {not available when compiled without the |+cmdline_hist| feature} @@ -463,11 +469,16 @@ When repeating 'wildchar' or CTRL-N you cycle through the matches, eventually ending up back to what was typed. If the first match is not what you wanted, you can use or CTRL-P to go straight back to what you typed. -The 'wildignorecase' option can be set to ignore case in filenames. - The 'wildmenu' option can be set to show the matches just above the command line. +The 'wildoptions' option provides additional configuration to use a popup menu +for 'wildmenu', and to use fuzzy matching. + +The 'wildignorecase' option can be set to ignore case in filenames. For +completing other texts (e.g. command names), the 'ignorecase' option is used +instead (fuzzy matching always ignores case, however). + If you like tcsh's autolist completion, you can use this mapping: :cnoremap X (Where X is the command key to use, is CTRL-L and is CTRL-D) diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index a302c59fe9..e7e99e2ec0 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1112,6 +1112,22 @@ tag command action in Command-line editing mode ~ |c_| toggle insert/overstrike mode |c_| cursor at mouse click +commands in wildmenu mode (see 'wildmenu') + + move up to parent / select the previous match + move down to submenu / select the next match + select the previous match / move up to parent + select the next match / move down to submenu + move into submenu when doing menu completion + other stop completion and insert the typed character + +commands in wildmenu mode with 'wildoptions' set to "pum" + + CTRL-E stop completion and go back to original text + CTRL-Y accept selected match and stop completion + select a match several entries back + select a match several entries forward + ============================================================================== 5. Terminal-Job mode *terminal-job-index* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index cfef59de60..502e77ba1f 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4440,8 +4440,8 @@ A jump table for the options with a short description can be found at |Q_op|. *'ignorecase'* *'ic'* *'noignorecase'* *'noic'* 'ignorecase' 'ic' boolean (default off) global - Ignore case in search patterns. Also used when searching in the tags - file. + Ignore case in search patterns, |cmdline-completion|, when + searching in the tags file, and non-|Vim9| |expr-==|. Also see 'smartcase' and 'tagcase'. Can be overruled by using "\c" or "\C" in the pattern, see |/ignorecase|. @@ -9314,29 +9314,32 @@ A jump table for the options with a short description can be found at |Q_op|. as needed. The "wildmenu" mode is abandoned when a key is hit that is not used for selecting a completion. - 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 - subdirectory or submenu. + While the "wildmenu" is active, the following keys have special + meanings: + CTRL-P - go to the previous entry + CTRL-N - go to the next entry - in menu completion, when the cursor is just after a dot: move into a submenu. + + When not using the popup menu for command line completion, the + following keys have special meanings: + - select previous/next match (like CTRL-P/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. When using the popup menu for command line completion, the following keys have special meanings: - - select next match (like CTRL-N) + - select previous/next match (like CTRL-P/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) + - Select a match several entries back, but don't insert it. + - Select a match several entries further, but don't insert it. 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. From 0f058d13206665bad37c7d42834cfa0075f50239 Mon Sep 17 00:00:00 2001 From: Ernie Rael Date: Sat, 14 Oct 2023 11:25:04 +0200 Subject: [PATCH 32/47] patch 9.0.2020: Vim9: islocked() needs more work Problem: Vim9: islocked() needs more work Solution: rework islocked() and remove sync_root from get_lval() closes: #13329 Signed-off-by: Christian Brabandt Co-authored-by: Ernie Rael --- src/eval.c | 36 ++++++------------------------- src/evalfunc.c | 58 +++++++++++++++++--------------------------------- src/structs.h | 12 ++++------- src/version.c | 2 ++ 4 files changed, 32 insertions(+), 76 deletions(-) diff --git a/src/eval.c b/src/eval.c index 80ff5a3cba..46eec35572 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1187,7 +1187,6 @@ get_lval( char buf[80]; ch_log(NULL, "LKVAR: ...: GLV flags: %s", flags_tostring(flags, glv_flag_strings, buf, sizeof(buf))); - int log_sync_root_key = FALSE; #endif // Clear everything in "lp". @@ -1326,21 +1325,16 @@ get_lval( } } - int sync_root = FALSE; - if (vim9script && lval_root != NULL) - { - cl_exec = lval_root->lr_cl_exec; - sync_root = lval_root->lr_sync_root; - } - - // Without [idx] or .key we are done, unless doing sync_root. - if (*p != '[' && *p != '.' && (*name == NUL || !sync_root)) + // Without [idx] or .key we are done. + if (*p != '[' && *p != '.') { if (lval_root != NULL) fill_lval_from_lval_root(lp, lval_root); return p; } + if (vim9script && lval_root != NULL) + cl_exec = lval_root->lr_cl_exec; if (vim9script && lval_root != NULL && lval_root->lr_tv != NULL) { // using local variable @@ -1375,7 +1369,7 @@ get_lval( */ var1.v_type = VAR_UNKNOWN; var2.v_type = VAR_UNKNOWN; - while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.') || sync_root) + while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { vartype_T v_type = lp->ll_tv->v_type; @@ -1439,19 +1433,7 @@ get_lval( } len = -1; - if (sync_root) - { - // For example, the first token is a member variable name and - // lp->ll_tv is a class/object. - // Process it directly without looking for "[idx]" or ".name". - key = name; - sync_root = FALSE; // only first time through -#ifdef LOG_LOCKVAR - log_sync_root_key = TRUE; - ch_log(NULL, "LKVAR: ... loop: name: %s, sync_root", name); -#endif - } - else if (*p == '.') + if (*p == '.') { key = p + 1; for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) @@ -1543,15 +1525,11 @@ get_lval( ++p; } #ifdef LOG_LOCKVAR - if (log_sync_root_key) - ch_log(NULL, "LKVAR: ... loop: p: %s, sync_root key: %s", p, - key); - else if (len == -1) + if (len == -1) ch_log(NULL, "LKVAR: ... loop: p: %s, '[' key: %s", p, empty1 ? ":" : (char*)tv_get_string(&var1)); else ch_log(NULL, "LKVAR: ... loop: p: %s, '.' key: %s", p, key); - log_sync_root_key = FALSE; #endif if (v_type == VAR_DICT) diff --git a/src/evalfunc.c b/src/evalfunc.c index 85c64a23a2..f9b81c6054 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -7325,64 +7325,48 @@ free_lval_root(lval_root_T *root) /* * This is used if executing in a method, the argument string is a - * variable/item expr/reference. If it starts with a potential class/object - * variable then return OK, may get later errors in get_lval. + * variable/item expr/reference. It may start with a potential class/object + * variable. * - * Adjust "root" as needed. Note that name may change (for example to skip - * "this") and is returned. lr_tv may be changed or freed. + * Adjust "root" as needed; lr_tv may be changed or freed. * * Always returns OK. * Free resources and return FAIL if the root should not be used. Otherwise OK. */ static int -fix_variable_reference_lval_root(lval_root_T *root, char_u **p_name) +fix_variable_reference_lval_root(lval_root_T *root, char_u *name) { - char_u *name = *p_name; - char_u *end; - dictitem_T *di; - // Only set lr_sync_root and lr_tv if the name is an object/class - // reference: object ("this.") or class because name is class variable. + // Check if lr_tv is the name of an object/class reference: name start with + // "this" or name is class variable. Clear lr_tv if neither. + int found_member = FALSE; if (root->lr_tv->v_type == VAR_OBJECT) { - if (STRNCMP("this.", name, 5) == 0) - { - name += 5; - root->lr_sync_root = TRUE; - } - else if (STRCMP("this", name) == 0) - { - name += 4; - root->lr_sync_root = TRUE; - } + if (STRNCMP("this.", name, 5) == 0 ||STRCMP("this", name) == 0) + found_member = TRUE; } - if (!root->lr_sync_root) // not object member, try class member + if (!found_member) // not object member, try class member { // Explicitly check if the name is a class member. // If it's not then do nothing. + char_u *end; for (end = name; ASCII_ISALNUM(*end) || *end == '_'; ++end) ; - if (class_member_lookup(root->lr_cl_exec, name, end - name, NULL) - != NULL) + int idx = class_member_idx(root->lr_cl_exec, name, end - name); + if (idx >= 0) { - // Using a class, so reference the class tv. - di = find_var(root->lr_cl_exec->class_name, NULL, FALSE); - if (di != NULL) - { - // replace the lr_tv - clear_tv(root->lr_tv); - copy_tv(&di->di_tv, root->lr_tv); - root->lr_sync_root = TRUE; - } + // A class variable, replace lr_tv with it + clear_tv(root->lr_tv); + copy_tv(&root->lr_cl_exec->class_members_tv[idx], root->lr_tv); + found_member = TRUE; } } - if (!root->lr_sync_root) + if (!found_member) { free_tv(root->lr_tv); root->lr_tv = NULL; // Not a member variable } - *p_name = name; // If FAIL, then must free_lval_root(root); return OK; } @@ -7415,12 +7399,8 @@ f_islocked(typval_T *argvars, typval_T *rettv) { // Almost always produces a valid lval_root since lr_cl_exec is used // for access verification, lr_tv may be set to NULL. - char_u *tname = name; - if (fix_variable_reference_lval_root(&aroot, &tname) == OK) - { - name = tname; + if (fix_variable_reference_lval_root(&aroot, name) == OK) root = &aroot; - } } lval_root_T *lval_root_save = lval_root; diff --git a/src/structs.h b/src/structs.h index a221a0406d..3f461f8514 100644 --- a/src/structs.h +++ b/src/structs.h @@ -4605,16 +4605,12 @@ typedef struct lval_S } lval_T; /** - * This may be used to specify the base typval that get_lval() uses when - * following a chain, for example a[idx1][idx2]. - * The lr_sync_root flags signals get_lval that the first time through - * the indexing loop, skip handling '.' and '[idx]'. + * This specifies optional parameters for get_lval(). Arguments may be NULL. */ typedef struct lval_root_S { - typval_T *lr_tv; - class_T *lr_cl_exec; // executing class for access checking - int lr_is_arg; - int lr_sync_root; + typval_T *lr_tv; // Base typval. + class_T *lr_cl_exec; // Executing class for access checking. + int lr_is_arg; // name is an arg (not a member). } lval_root_T; // Structure used to save the current state. Used when executing Normal mode diff --git a/src/version.c b/src/version.c index 9147441a40..afcdadfbb1 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2020, /**/ 2019, /**/ From 7687238e1b0d2f26ba57e1bdf76f782eaa43af3a Mon Sep 17 00:00:00 2001 From: dkearns Date: Sat, 14 Oct 2023 20:27:27 +1100 Subject: [PATCH 33/47] runtime(tcsh): Update ftplugin (#13327) Fix b:browsefilter deletion error when calling b:undo_ftplugin. Fixes #13167 Signed-off-by: Christian Brabandt --- runtime/ftplugin/csh.vim | 17 ++++++++++------- runtime/ftplugin/tcsh.vim | 20 ++++++++++++-------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/runtime/ftplugin/csh.vim b/runtime/ftplugin/csh.vim index ca5da5a8b9..2feec57bb2 100644 --- a/runtime/ftplugin/csh.vim +++ b/runtime/ftplugin/csh.vim @@ -3,9 +3,11 @@ " Maintainer: Doug Kearns " Previous Maintainer: Dan Sharp " Contributor: Johannes Zellner -" Last Change: 2021 Oct 15 +" Last Change: 2023 Oct 09 -if exists("b:did_ftplugin") | finish | endif +if exists("b:did_ftplugin") + finish +endif let b:did_ftplugin = 1 let s:save_cpo = &cpo @@ -18,7 +20,7 @@ setlocal formatoptions+=crql let b:undo_ftplugin = "setlocal com< cms< fo<" -" Csh: thanks to Johannes Zellner +" Csh: thanks to Johannes Zellner " - Both foreach and end must appear alone on separate lines. " - The words else and endif must appear at the beginning of input lines; " the if must appear alone on its input line or after an else. @@ -38,13 +40,14 @@ if exists("loaded_matchit") && !exists("b:match_words") \ s:line_start .. 'case\s\+:' .. s:line_start .. 'default\>:\:' .. \ s:line_start .. 'endsw\>' unlet s:line_start - let b:undo_ftplugin ..= " | unlet b:match_words" + let b:undo_ftplugin ..= " | unlet! b:match_words" endif if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") - let b:browsefilter="csh Scripts (*.csh)\t*.csh\n" .. - \ "All Files (*.*)\t*.*\n" - let b:undo_ftplugin ..= " | unlet b:browsefilter" + let b:browsefilter = "csh Scripts (*.csh)\t*.csh\n" .. + \ "All Files (*.*)\t*.*\n" + let b:csh_set_browsefilter = 1 + let b:undo_ftplugin ..= " | unlet! b:browsefilter b:csh_set_browsefilter" endif let &cpo = s:save_cpo diff --git a/runtime/ftplugin/tcsh.vim b/runtime/ftplugin/tcsh.vim index 85d3873b33..b2467b43a2 100644 --- a/runtime/ftplugin/tcsh.vim +++ b/runtime/ftplugin/tcsh.vim @@ -2,9 +2,11 @@ " Language: tcsh " Maintainer: Doug Kearns " Previous Maintainer: Dan Sharp -" Last Change: 2021 Oct 15 +" Last Change: 2023 Oct 09 -if exists("b:did_ftplugin") | finish | endif +if exists("b:did_ftplugin") + finish +endif let s:save_cpo = &cpo set cpo-=C @@ -12,24 +14,26 @@ set cpo-=C " Define some defaults in case the included ftplugins don't set them. let s:undo_ftplugin = "" let s:browsefilter = "csh Files (*.csh)\t*.csh\n" .. - \ "All Files (*.*)\t*.*\n" + \ "All Files (*.*)\t*.*\n" runtime! ftplugin/csh.vim ftplugin/csh_*.vim ftplugin/csh/*.vim let b:did_ftplugin = 1 " Override our defaults if these were set by an included ftplugin. if exists("b:undo_ftplugin") - let s:undo_ftplugin = b:undo_ftplugin + let s:undo_ftplugin = b:undo_ftplugin endif if exists("b:browsefilter") - let s:browsefilter = b:browsefilter + let s:browsefilter = b:browsefilter endif -if (has("gui_win32") || has("gui_gtk")) - let b:browsefilter="tcsh Scripts (*.tcsh)\t*.tcsh\n" .. s:browsefilter +if (has("gui_win32") || has("gui_gtk")) && + \ (!exists("b:browsefilter") || exists("b:csh_set_browsefilter")) + let b:browsefilter = "tcsh Scripts (*.tcsh)\t*.tcsh\n" .. s:browsefilter + let s:undo_ftplugin = "unlet! b:browsefilter | " .. s:undo_ftplugin endif -let b:undo_ftplugin = "unlet! b:browsefilter | " .. s:undo_ftplugin +let b:undo_ftplugin = s:undo_ftplugin let &cpo = s:save_cpo unlet s:save_cpo From 5ae6f9985ed52aa023baca99793dc5c1d62780c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Pe=C5=A1i=C4=87?= <27575106+eevan78@users.noreply.github.com> Date: Sat, 14 Oct 2023 13:28:05 +0400 Subject: [PATCH 34/47] translation(sr): Update Serbian messages translation (#13324) Signed-off-by: Christian Brabandt --- src/po/sr.po | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/po/sr.po b/src/po/sr.po index 48a4eb5d26..7536e6b664 100644 --- a/src/po/sr.po +++ b/src/po/sr.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim(Serbian)\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-10-05 16:19+0400\n" +"POT-Creation-Date: 2023-10-12 14:11+0400\n" "PO-Revision-Date: 2023-10-05 16:31+0400\n" "Last-Translator: Ivan Pešić \n" "Language-Team: Serbian\n" @@ -6164,8 +6164,9 @@ msgstr "E687: Мање одредишта него ставки Листе" msgid "E688: More targets than List items" msgstr "E688: Више одредишта него ставки Листе" -msgid "E689: Can only index a List, Dictionary or Blob" -msgstr "E689: Само Листа, Речник или Блоб могу да се индексирају" +#, c-format +msgid "E689: Index not allowed after a %s: %s" +msgstr "E689: Индекс није дозвољен након %s: %s" msgid "E690: Missing \"in\" after :for" msgstr "E690: Недостаје „in” након :for" @@ -7979,8 +7980,8 @@ msgid "E1202: No white space allowed after '%s': %s" msgstr "E1202: Празан простор није дозвољен након ’%s’: %s" #, c-format -msgid "E1203: Dot can only be used on a dictionary: %s" -msgstr "E1203: Тачка може да се користи само над речником: %s" +msgid "E1203: Dot not allowed after a %s: %s" +msgstr "E1203: Тачка није дозвољена након %s: %s" #, c-format msgid "E1204: No Number allowed after .: '\\%%%c'" @@ -8667,7 +8668,8 @@ msgstr "E1391: Не може да се откључа/закључа проме #, c-format msgid "E1392: Cannot (un)lock class variable \"%s\" in class \"%s\"" -msgstr "E1392: Не може да се откључа/закључа променљива класе „%s” у класи „%s”" +msgstr "" +"E1392: Не може да се откључа/закључа променљива класе „%s” у класи „%s”" #, c-format msgid "E1500: Cannot mix positional and non-positional arguments: %s" @@ -8708,7 +8710,8 @@ msgstr "" "дозвољене величине" msgid "E1509: Error occured when reading or writing extended attribute" -msgstr "E1509: Дошло је до грешке приликом читања или уписивања проширеног атрибута" +msgstr "" +"E1509: Дошло је до грешке приликом читања или уписивања проширеног атрибута" msgid "--No lines in buffer--" msgstr "--У баферу нема линија--" From cd6ee6935811ab223605a3f39a550d26a617867d Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 14 Oct 2023 11:29:28 +0200 Subject: [PATCH 35/47] patch 9.0.2021: Coverity complains about change in charset Problem: Coverity complains about change in charset (after v9.0.2017) Solution: check pointer t at index 0 closes: #13322 Signed-off-by: Christian Brabandt --- src/charset.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/charset.c b/src/charset.c index 50a81cccd9..bda3f911b6 100644 --- a/src/charset.c +++ b/src/charset.c @@ -1348,7 +1348,7 @@ win_lbr_chartabsize( if (wp->w_p_lbr && wp->w_p_wrap && wp->w_width != 0) { char_u *t = cts->cts_line; - while (VIM_ISBREAK((int)*t)) + while (VIM_ISBREAK((int)t[0])) t++; vcol_start = t - cts->cts_line; } diff --git a/src/version.c b/src/version.c index afcdadfbb1..2c9354634b 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2021, /**/ 2020, /**/ From b583eda7031b1f6a3469a2537d0c10ca5fa5568e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 14 Oct 2023 11:32:28 +0200 Subject: [PATCH 36/47] patch 9.0.2022: getmousepos() returns wrong index for TAB char Problem: When clicking in the middle of a TAB, getmousepos() returns the column of the next char instead of the TAB. Solution: Break out of the loop when the vcol to find is inside current char. Fix invalid memory access when calling virtcol2col() on an empty line. closes: #13321 Signed-off-by: Christian Brabandt Co-authored-by: zeertzjq --- runtime/doc/builtin.txt | 2 ++ src/mouse.c | 7 ++++-- src/move.c | 13 +++++++---- src/testdir/test_cursor_func.vim | 5 ++++ src/testdir/test_functions.vim | 40 ++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 6 files changed, 62 insertions(+), 7 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 029c08bbd9..f621fc0516 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -10376,6 +10376,8 @@ virtcol2col({winid}, {lnum}, {col}) *virtcol2col()* character in window {winid} at buffer line {lnum} and virtual column {col}. + If buffer line {lnum} is an empty line, 0 is returned. + If {col} is greater than the last virtual column in line {lnum}, then the byte index of the character at the last virtual column is returned. diff --git a/src/mouse.c b/src/mouse.c index f3342f9056..fe5c14cac5 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -3201,7 +3201,7 @@ mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED) || defined(FEAT_EVAL) || defined(PROTO) /* * Convert a virtual (screen) column to a character column. - * The first column is one. + * The first column is zero. */ int vcol2col(win_T *wp, linenr_T lnum, int vcol) @@ -3214,7 +3214,10 @@ vcol2col(win_T *wp, linenr_T lnum, int vcol) init_chartabsize_arg(&cts, wp, lnum, 0, line, line); while (cts.cts_vcol < vcol && *cts.cts_ptr != NUL) { - cts.cts_vcol += win_lbr_chartabsize(&cts, NULL); + int size = win_lbr_chartabsize(&cts, NULL); + if (cts.cts_vcol + size > vcol) + break; + cts.cts_vcol += size; MB_PTR_ADV(cts.cts_ptr); } clear_chartabsize_arg(&cts); diff --git a/src/move.c b/src/move.c index 42878e4ad9..72490d2f85 100644 --- a/src/move.c +++ b/src/move.c @@ -1547,14 +1547,17 @@ f_screenpos(typval_T *argvars UNUSED, typval_T *rettv) static int virtcol2col(win_T *wp, linenr_T lnum, int vcol) { - int offset = vcol2col(wp, lnum, vcol); + int offset = vcol2col(wp, lnum, vcol - 1); char_u *line = ml_get_buf(wp->w_buffer, lnum, FALSE); char_u *p = line + offset; - // For a multibyte character, need to return the column number of the first - // byte. - MB_PTR_BACK(line, p); - + if (*p == NUL) + { + if (p == line) // empty line + return 0; + // Move to the first byte of the last char. + MB_PTR_BACK(line, p); + } return (int)(p - line + 1); } diff --git a/src/testdir/test_cursor_func.vim b/src/testdir/test_cursor_func.vim index 4fc59288c0..3cdf4cb7f9 100644 --- a/src/testdir/test_cursor_func.vim +++ b/src/testdir/test_cursor_func.vim @@ -573,6 +573,11 @@ func Test_virtcol2col() call assert_equal(8, virtcol2col(0, 1, 7)) call assert_equal(8, virtcol2col(0, 1, 8)) + " These used to cause invalid memory access + call setline(1, '') + call assert_equal(0, virtcol2col(0, 1, 1)) + call assert_equal(0, virtcol2col(0, 1, 2)) + let w = winwidth(0) call setline(2, repeat('a', w + 2)) let win_nosbr = win_getid() diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index bf0bf9036d..50b7fb23ca 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -3366,6 +3366,46 @@ func Test_getmousepos() \ line: 1, \ column: 1, \ }, getmousepos()) + call test_setmouse(1, 2) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 2, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 2, + \ line: 1, + \ column: 1, + \ }, getmousepos()) + call test_setmouse(1, 8) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 8, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 8, + \ line: 1, + \ column: 1, + \ }, getmousepos()) + call test_setmouse(1, 9) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 9, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 9, + \ line: 1, + \ column: 2, + \ }, getmousepos()) + call test_setmouse(1, 12) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 12, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 12, + \ line: 1, + \ column: 2, + \ }, getmousepos()) call test_setmouse(1, 25) call assert_equal(#{ \ screenrow: 1, diff --git a/src/version.c b/src/version.c index 2c9354634b..d842108880 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2022, /**/ 2021, /**/ From ba9aed44975cc82290208fd45f35dd72fc66d6c1 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 14 Oct 2023 11:38:12 +0200 Subject: [PATCH 37/47] patch 9.0.2023: need more tests for :cq Problem: need more tests for :cq Solution: Add more tests, including wraparound on linux Signed-off-by: Christian Brabandt --- src/testdir/test_startup.vim | 20 ++++++++++++++++++++ src/version.c | 2 ++ 2 files changed, 22 insertions(+) diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim index 949553350f..6780205a2d 100644 --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -1351,5 +1351,25 @@ func Test_rename_buffer_on_startup() call delete('Xresult') endfunc +" Test that -cq works as expected +func Test_cq_zero_exmode() + let logfile = 'Xcq_log.txt' + let out = system(GetVimCommand() .. ' --clean --log ' .. logfile .. ' -es -X -c "argdelete foobar" -c"7cq"') + call assert_equal(8, v:shell_error) + let log = filter(readfile(logfile), {idx, val -> val =~ "E480"}) + call assert_match('E480: No match: foobar', log[0]) + call delete(logfile) + + " wrap-around on Unix + let out = system(GetVimCommand() .. ' --clean --log ' .. logfile .. ' -es -X -c "argdelete foobar" -c"255cq"') + if !has('win32') + call assert_equal(0, v:shell_error) + else + call assert_equal(256, v:shell_error) + endif + let log = filter(readfile(logfile), {idx, val -> val =~ "E480"}) + call assert_match('E480: No match: foobar', log[0]) + call delete('Xcq_log.txt') +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index d842108880..8eb32e6638 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2023, /**/ 2022, /**/ From bd734c3bead9e167eb6875f62cc06fab2379c422 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sat, 14 Oct 2023 11:41:34 +0200 Subject: [PATCH 38/47] patch 9.0.2024: no filetype detection for Debian sources Problem: no filetype detection for Debian sources Solution: Add new deb822sources filetype closes: #13320 Signed-off-by: Christian Brabandt Co-authored-by: James McCoy --- runtime/filetype.vim | 1 + src/testdir/test_filetype.vim | 1 + src/version.c | 2 ++ 3 files changed, 4 insertions(+) diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 700fd6a61a..c25b89f553 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -544,6 +544,7 @@ au BufNewFile,BufRead copyright " Debian Sources.list au BufNewFile,BufRead */etc/apt/sources.list setf debsources au BufNewFile,BufRead */etc/apt/sources.list.d/*.list setf debsources +au BufNewFile,BufRead */etc/apt/sources.list.d/*.sources setf deb822sources " Deny hosts au BufNewFile,BufRead denyhosts.conf setf denyhosts diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 78e04d3848..dbaf9696f1 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -194,6 +194,7 @@ def s:GetFilenameChecks(): dict> debcontrol: ['/debian/control', 'any/debian/control'], debcopyright: ['/debian/copyright', 'any/debian/copyright'], debsources: ['/etc/apt/sources.list', '/etc/apt/sources.list.d/file.list', 'any/etc/apt/sources.list', 'any/etc/apt/sources.list.d/file.list'], + deb822sources: ['/etc/apt/sources.list.d/file.sources', 'any/etc/apt/sources.list.d/file.sources'], def: ['file.def'], denyhosts: ['denyhosts.conf'], desc: ['file.desc'], diff --git a/src/version.c b/src/version.c index 8eb32e6638..09a6b69eb9 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2024, /**/ 2023, /**/ From 989426be6e9ae23d2413943890206cbe15d9df38 Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Sat, 14 Oct 2023 11:46:51 +0200 Subject: [PATCH 39/47] patch 9.0.2025: no cmdline completion for ++opt args Problem: no cmdline completion for ++opt args Solution: Add cmdline completion for :e ++opt=arg and :terminal [++options] closes: #13319 Signed-off-by: Christian Brabandt Co-authored-by: Yee Cheng Chin --- runtime/doc/cmdline.txt | 1 + src/cmdexpand.c | 76 +++++++++++++++++++++--- src/ex_docmd.c | 111 +++++++++++++++++++++++++++++++++++ src/optionstr.c | 13 ++++ src/proto/ex_docmd.pro | 1 + src/proto/optionstr.pro | 1 + src/proto/terminal.pro | 1 + src/structs.h | 3 +- src/terminal.c | 92 +++++++++++++++++++++++++++++ src/testdir/test_cmdline.vim | 40 +++++++++++++ src/version.c | 2 + src/vim.h | 2 + 12 files changed, 335 insertions(+), 8 deletions(-) diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 6d58d63873..c5d0096ddb 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -387,6 +387,7 @@ When editing the command-line, a few commands can be used to complete the word before the cursor. This is available for: - Command names: At the start of the command-line. +- |++opt| values. - Tags: Only after the ":tag" command. - File names: Only after a command that accepts a file name or a setting for an option that can be set to a file name. This is called file name diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 20f3069ce2..d27e039443 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -1768,6 +1768,45 @@ set_context_for_wildcard_arg( } } +/* + * Set the completion context for the "++opt=arg" argument. Always returns + * NULL. + */ + static char_u * +set_context_in_argopt(expand_T *xp, char_u *arg) +{ + char_u *p; + + p = vim_strchr(arg, '='); + if (p == NULL) + xp->xp_pattern = arg; + else + xp->xp_pattern = p + 1; + + xp->xp_context = EXPAND_ARGOPT; + return NULL; +} + +#ifdef FEAT_TERMINAL +/* + * Set the completion context for :terminal's [options]. Always returns NULL. + */ + static char_u * +set_context_in_terminalopt(expand_T *xp, char_u *arg) +{ + char_u *p; + + p = vim_strchr(arg, '='); + if (p == NULL) + xp->xp_pattern = arg; + else + xp->xp_pattern = p + 1; + + xp->xp_context = EXPAND_TERMINALOPT; + return NULL; +} +#endif + /* * Set the completion context for the :filter command. Returns a pointer to the * next command after the :filter command. @@ -2491,13 +2530,28 @@ set_one_cmd_context( arg = skipwhite(p); - // Skip over ++argopt argument - if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0) + // Does command allow "++argopt" argument? + if ((ea.argt & EX_ARGOPT) || ea.cmdidx == CMD_terminal) { - p = arg; - while (*p && !vim_isspace(*p)) - MB_PTR_ADV(p); - arg = skipwhite(p); + while (*arg != NUL && STRNCMP(arg, "++", 2) == 0) + { + p = arg + 2; + while (*p && !vim_isspace(*p)) + MB_PTR_ADV(p); + + // Still touching the command after "++"? + if (*p == NUL) + { + if (ea.argt & EX_ARGOPT) + return set_context_in_argopt(xp, arg + 2); +#ifdef FEAT_TERMINAL + if (ea.cmdidx == CMD_terminal) + return set_context_in_terminalopt(xp, arg + 2); +#endif + } + + arg = skipwhite(p); + } } if (ea.cmdidx == CMD_write || ea.cmdidx == CMD_update) @@ -3120,6 +3174,12 @@ ExpandFromContext( ret = ExpandSettingSubtract(xp, ®match, numMatches, matches); else if (xp->xp_context == EXPAND_MAPPINGS) ret = ExpandMappings(pat, ®match, numMatches, matches); + else if (xp->xp_context == EXPAND_ARGOPT) + ret = expand_argopt(pat, xp, ®match, matches, numMatches); +#if defined(FEAT_TERMINAL) + else if (xp->xp_context == EXPAND_TERMINALOPT) + ret = expand_terminal_opt(pat, xp, ®match, matches, numMatches); +#endif #if defined(FEAT_EVAL) else if (xp->xp_context == EXPAND_USER_DEFINED) ret = ExpandUserDefined(pat, xp, ®match, matches, numMatches); @@ -3253,7 +3313,9 @@ ExpandGeneric( if (!fuzzy && xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_STRING_SETTING && xp->xp_context != EXPAND_MENUS - && xp->xp_context != EXPAND_SCRIPTNAMES) + && xp->xp_context != EXPAND_SCRIPTNAMES + && xp->xp_context != EXPAND_ARGOPT + && xp->xp_context != EXPAND_TERMINALOPT) sort_matches = TRUE; // functions should be sorted to the end. diff --git a/src/ex_docmd.c b/src/ex_docmd.c index d4b972a2ef..f0c7aad7df 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -5407,6 +5407,25 @@ get_bad_opt(char_u *p, exarg_T *eap) return OK; } +/* + * Function given to ExpandGeneric() to obtain the list of bad= names. + */ + static char_u * +get_bad_name(expand_T *xp UNUSED, int idx) +{ + // Note: Keep this in sync with getargopt. + static char *(p_bad_values[]) = + { + "?", + "keep", + "drop", + }; + + if (idx < (int)ARRAY_LENGTH(p_bad_values)) + return (char_u*)p_bad_values[idx]; + return NULL; +} + /* * Get "++opt=arg" argument. * Return FAIL or OK. @@ -5419,6 +5438,8 @@ getargopt(exarg_T *eap) int bad_char_idx; char_u *p; + // Note: Keep this in sync with get_argopt_name. + // ":edit ++[no]bin[ary] file" if (STRNCMP(arg, "bin", 3) == 0 || STRNCMP(arg, "nobin", 5) == 0) { @@ -5499,6 +5520,96 @@ getargopt(exarg_T *eap) return OK; } +/* + * Function given to ExpandGeneric() to obtain the list of ++opt names. + */ + static char_u * +get_argopt_name(expand_T *xp UNUSED, int idx) +{ + // Note: Keep this in sync with getargopt. + static char *(p_opt_values[]) = + { + "fileformat=", + "encoding=", + "binary", + "nobinary", + "bad=", + "edit", + }; + + if (idx < (int)ARRAY_LENGTH(p_opt_values)) + return (char_u*)p_opt_values[idx]; + return NULL; +} + +/* + * Command-line expansion for ++opt=name. + */ + int +expand_argopt( + char_u *pat, + expand_T *xp, + regmatch_T *rmp, + char_u ***matches, + int *numMatches) +{ + if (xp->xp_pattern > xp->xp_line && *(xp->xp_pattern-1) == '=') + { + char_u *(*cb)(expand_T *, int) = NULL; + + char_u *name_end = xp->xp_pattern - 1; + if (name_end - xp->xp_line >= 2 + && STRNCMP(name_end - 2, "ff", 2) == 0) + cb = get_fileformat_name; + else if (name_end - xp->xp_line >= 10 + && STRNCMP(name_end - 10, "fileformat", 10) == 0) + cb = get_fileformat_name; + else if (name_end - xp->xp_line >= 3 + && STRNCMP(name_end - 3, "enc", 3) == 0) + cb = get_encoding_name; + else if (name_end - xp->xp_line >= 8 + && STRNCMP(name_end - 8, "encoding", 8) == 0) + cb = get_encoding_name; + else if (name_end - xp->xp_line >= 3 + && STRNCMP(name_end - 3, "bad", 3) == 0) + cb = get_bad_name; + + if (cb != NULL) + { + return ExpandGeneric( + pat, + xp, + rmp, + matches, + numMatches, + cb, + FALSE); + } + return FAIL; + } + + // Special handling of "ff" which acts as a short form of + // "fileformat", as "ff" is not a substring of it. + if (STRCMP(xp->xp_pattern, "ff") == 0) + { + *matches = ALLOC_MULT(char_u *, 1); + if (*matches == NULL) + return FAIL; + *numMatches = 1; + (*matches)[0] = vim_strsave((char_u*)"fileformat="); + return OK; + } + + return ExpandGeneric( + pat, + xp, + rmp, + matches, + numMatches, + get_argopt_name, + FALSE); +} + static void ex_autocmd(exarg_T *eap) { diff --git a/src/optionstr.c b/src/optionstr.c index 202e93e57d..8458f2a4a4 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -2104,6 +2104,19 @@ expand_set_fileformat(optexpand_T *args, int *numMatches, char_u ***matches) matches); } +/* + * Function given to ExpandGeneric() to obtain the possible arguments of the + * fileformat options. + */ + char_u * +get_fileformat_name(expand_T *xp UNUSED, int idx) +{ + if (idx >= (int)ARRAY_LENGTH(p_ff_values)) + return NULL; + + return (char_u*)p_ff_values[idx]; +} + /* * The 'fileformats' option is changed. */ diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index 3a1dc5fe4d..3fd20b27a7 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -30,6 +30,7 @@ int expand_filename(exarg_T *eap, char_u **cmdlinep, char **errormsgp); void separate_nextcmd(exarg_T *eap, int keep_backslash); char_u *skip_cmd_arg(char_u *p, int rembs); int get_bad_opt(char_u *p, exarg_T *eap); +int expand_argopt(char_u *pat, expand_T *xp, regmatch_T *rmp, char_u ***matches, int *numMatches); int ends_excmd(int c); int ends_excmd2(char_u *cmd_start, char_u *cmd); char_u *find_nextcmd(char_u *p); diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro index 88034fcec5..22601ba996 100644 --- a/src/proto/optionstr.pro +++ b/src/proto/optionstr.pro @@ -189,6 +189,7 @@ int expand_set_wildoptions(optexpand_T *args, int *numMatches, char_u ***matches int expand_set_winaltkeys(optexpand_T *args, int *numMatches, char_u ***matches); int expand_set_wincolor(optexpand_T *args, int *numMatches, char_u ***matches); int check_ff_value(char_u *p); +char_u *get_fileformat_name(expand_T *xp, int idx); void save_clear_shm_value(void); void restore_shm_value(void); /* vim: set ft=c : */ diff --git a/src/proto/terminal.pro b/src/proto/terminal.pro index f7ba72b761..dfa59cced1 100644 --- a/src/proto/terminal.pro +++ b/src/proto/terminal.pro @@ -2,6 +2,7 @@ void init_job_options(jobopt_T *opt); buf_T *term_start(typval_T *argvar, char **argv, jobopt_T *opt, int flags); void ex_terminal(exarg_T *eap); +int expand_terminal_opt(char_u *pat, expand_T *xp, regmatch_T *rmp, char_u ***matches, int *numMatches); int term_write_session(FILE *fd, win_T *wp, hashtab_T *terminal_bufs); int term_should_restore(buf_T *buf); void free_terminal(buf_T *buf); diff --git a/src/structs.h b/src/structs.h index 3f461f8514..f7f3b2ec56 100644 --- a/src/structs.h +++ b/src/structs.h @@ -603,7 +603,8 @@ typedef enum { */ typedef struct expand { - char_u *xp_pattern; // start of item to expand + char_u *xp_pattern; // start of item to expand, guaranteed + // to be part of xp_line int xp_context; // type of expansion int xp_pattern_len; // bytes in xp_pattern before cursor xp_prefix_T xp_prefix; diff --git a/src/terminal.c b/src/terminal.c index 7156665714..f79d102e8c 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -818,6 +818,8 @@ ex_terminal(exarg_T *eap) ep = NULL; } + // Note: Keep this in sync with get_terminalopt_name. + # define OPTARG_HAS(name) ((int)(p - cmd) == sizeof(name) - 1 \ && STRNICMP(cmd, name, sizeof(name) - 1) == 0) if (OPTARG_HAS("close")) @@ -969,6 +971,96 @@ ex_terminal(exarg_T *eap) vim_free(opt.jo_eof_chars); } + static char_u * +get_terminalopt_name(expand_T *xp UNUSED, int idx) +{ + // Note: Keep this in sync with ex_terminal. + static char *(p_termopt_values[]) = + { + "close", + "noclose", + "open", + "curwin", + "hidden", + "norestore", + "shell", + "kill=", + "rows=", + "cols=", + "eof=", + "type=", + "api=", + }; + + if (idx < (int)ARRAY_LENGTH(p_termopt_values)) + return (char_u*)p_termopt_values[idx]; + return NULL; +} + + static char_u * +get_termkill_name(expand_T *xp UNUSED, int idx) +{ + // These are platform-specific values used for job_stop(). They are defined + // in each platform's mch_signal_job(). Just use a unified auto-complete + // list for simplicity. + static char *(p_termkill_values[]) = + { + "term", + "hup", + "quit", + "int", + "kill", + "winch", + }; + + if (idx < (int)ARRAY_LENGTH(p_termkill_values)) + return (char_u*)p_termkill_values[idx]; + return NULL; +} + +/* + * Command-line expansion for :terminal [options] + */ + int +expand_terminal_opt( + char_u *pat, + expand_T *xp, + regmatch_T *rmp, + char_u ***matches, + int *numMatches) +{ + if (xp->xp_pattern > xp->xp_line && *(xp->xp_pattern-1) == '=') + { + char_u *(*cb)(expand_T *, int) = NULL; + + char_u *name_end = xp->xp_pattern - 1; + if (name_end - xp->xp_line >= 4 + && STRNCMP(name_end - 4, "kill", 4) == 0) + cb = get_termkill_name; + + if (cb != NULL) + { + return ExpandGeneric( + pat, + xp, + rmp, + matches, + numMatches, + cb, + FALSE); + } + return FAIL; + } + return ExpandGeneric( + pat, + xp, + rmp, + matches, + numMatches, + get_terminalopt_name, + FALSE); +} + #if defined(FEAT_SESSION) || defined(PROTO) /* * Write a :terminal command to the session file to restore the terminal in diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index e79fa72bba..ddfeba28ff 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -1083,6 +1083,46 @@ func Test_cmdline_complete_expression() unlet g:SomeVar endfunc +func Test_cmdline_complete_argopt() + " completion for ++opt=arg for file commands + call assert_equal('fileformat=', getcompletion('edit ++', 'cmdline')[0]) + call assert_equal('encoding=', getcompletion('read ++e', 'cmdline')[0]) + call assert_equal('edit', getcompletion('read ++bin ++edi', 'cmdline')[0]) + + call assert_equal(['fileformat='], getcompletion('edit ++ff', 'cmdline')) + + call assert_equal('dos', getcompletion('write ++ff=d', 'cmdline')[0]) + call assert_equal('mac', getcompletion('args ++fileformat=m', 'cmdline')[0]) + call assert_equal('utf-8', getcompletion('split ++enc=ut*-8', 'cmdline')[0]) + call assert_equal('latin1', getcompletion('tabedit ++encoding=lati', 'cmdline')[0]) + call assert_equal('keep', getcompletion('edit ++bad=k', 'cmdline')[0]) + + call assert_equal([], getcompletion('edit ++bogus=', 'cmdline')) + + " completion should skip the ++opt and continue + call writefile([], 'Xaaaaa.txt', 'D') + call feedkeys(":split ++enc=latin1 Xaaa\\\"\", 'xt') + call assert_equal('"split ++enc=latin1 Xaaaaa.txt', @:) + + if has('terminal') + " completion for terminal's [options] + call assert_equal('close', getcompletion('terminal ++cl*e', 'cmdline')[0]) + call assert_equal('hidden', getcompletion('terminal ++open ++hidd', 'cmdline')[0]) + call assert_equal('term', getcompletion('terminal ++kill=ter', 'cmdline')[0]) + + call assert_equal([], getcompletion('terminal ++bogus=', 'cmdline')) + + " :terminal completion should skip the ++opt when considering what is the + " first option, which is a list of shell commands, unlike second option + " onwards. + let first_param = getcompletion('terminal ', 'cmdline') + let second_param = getcompletion('terminal foo ', 'cmdline') + let skipped_opt_param = getcompletion('terminal ++close ', 'cmdline') + call assert_equal(first_param, skipped_opt_param) + call assert_notequal(first_param, second_param) + endif +endfunc + " Unique function name for completion below func s:WeirdFunc() echo 'weird' diff --git a/src/version.c b/src/version.c index 09a6b69eb9..73a573df58 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2025, /**/ 2024, /**/ diff --git a/src/vim.h b/src/vim.h index 2bafda4249..2b6c787b99 100644 --- a/src/vim.h +++ b/src/vim.h @@ -824,6 +824,8 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring); #define EXPAND_RUNTIME 53 #define EXPAND_STRING_SETTING 54 #define EXPAND_SETTING_SUBTRACT 55 +#define EXPAND_ARGOPT 56 +#define EXPAND_TERMINALOPT 57 // Values for exmode_active (0 is no exmode) #define EXMODE_NORMAL 1 From ae3cfa47d3dcee75061db598eb19879693b2393a Mon Sep 17 00:00:00 2001 From: Ken Takata Date: Sat, 14 Oct 2023 11:49:09 +0200 Subject: [PATCH 40/47] patch 9.0.2026: win32: python3 dll loading can be improved Problem: win32: python3 dll loading can be improved Solution: Load DLL from registry path Support loading python3.dll and/or python3xx.dll from the path written in the registry. To support Stable ABI's forwarder DLL (python3.dll), use the `LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR` flag for `LoadLibraryExW()` because python3xx.dll is placed in the same directory of python3.dll. If Stable ABI is used, search the latest version from the registry (both from HKEY_CURRENT_USER and HKEY_LOCAL_MACHINE). If Stable ABI is not used, search only the matching version. closes: #13315 Signed-off-by: Christian Brabandt Co-authored-by: Ken Takata --- runtime/doc/if_pyth.txt | 8 ++- src/if_python3.c | 125 ++++++++++++++++++++++++++++++---------- src/version.c | 2 + 3 files changed, 101 insertions(+), 34 deletions(-) diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt index 3d3b92a662..c0b968e07c 100644 --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -754,8 +754,10 @@ you can use Vim without this file. MS-Windows ~ To use the Python interface the Python DLL must be in your search path. In a -console window type "path" to see what directories are used. The 'pythondll' -or 'pythonthreedll' option can be also used to specify the Python DLL. +console window type "path" to see what directories are used. If the DLL is +not found in your search path, Vim will check the registry to find the path +where Python is installed. The 'pythondll' or 'pythonthreedll' option can be +also used to specify the Python DLL. The name of the DLL should match the Python version Vim was compiled with. Currently the name for Python 2 is "python27.dll", that is for Python 2.7. @@ -782,6 +784,8 @@ and failures. With Stable ABI, this restriction is relaxed, and any Python 3 library with version of at least |v:python3_version| will work. See |has-python| for how to check if Stable ABI is supported, or see if version output includes |+python3/dyn-stable|. +On MS-Windows, 'pythonthreedll' will be set to "python3.dll". When searching +the DLL from the registry, Vim will search the latest version of Python. ============================================================================== 10. Python 3 *python3* diff --git a/src/if_python3.c b/src/if_python3.c index c3900892ea..a177371720 100644 --- a/src/if_python3.c +++ b/src/if_python3.c @@ -835,17 +835,16 @@ Py_ssize_t py3_PyList_GET_SIZE(PyObject *op) * Look up the library "libname" using the InstallPath registry key. * Return NULL when failed. Return an allocated string when successful. */ - static char * + static WCHAR * py3_get_system_libname(const char *libname) { + const WCHAR *pythoncore = L"Software\\Python\\PythonCore"; const char *cp = libname; - char subkey[128]; + WCHAR subkey[128]; HKEY hKey; - char installpath[MAXPATHL]; - LONG len = sizeof(installpath); - LSTATUS rc; - size_t sysliblen; - char *syslibname; + int i; + DWORD j, len; + LSTATUS ret; while (*cp != '\0') { @@ -857,35 +856,95 @@ py3_get_system_libname(const char *libname) } ++cp; } - vim_snprintf(subkey, sizeof(subkey), + + WCHAR keyfound[32]; + HKEY hKeyTop[] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE}; + HKEY hKeyFound = NULL; +# ifdef USE_LIMITED_API + long maxminor = -1; +# endif + for (i = 0; i < ARRAY_LENGTH(hKeyTop); i++) + { + long major, minor; + + ret = RegOpenKeyExW(hKeyTop[i], pythoncore, 0, KEY_READ, &hKey); + if (ret != ERROR_SUCCESS) + continue; + for (j = 0;; j++) + { + WCHAR keyname[32]; + WCHAR *wp; + + len = ARRAY_LENGTH(keyname); + ret = RegEnumKeyExW(hKey, j, keyname, &len, + NULL, NULL, NULL, NULL); + if (ret == ERROR_NO_MORE_ITEMS) + break; + + major = wcstol(keyname, &wp, 10); + if (*wp == L'.') + minor = wcstol(wp + 1, &wp, 10); # ifdef _WIN64 - "Software\\Python\\PythonCore\\%d.%d\\InstallPath", + if (*wp != L'\0') + continue; # else - "Software\\Python\\PythonCore\\%d.%d-32\\InstallPath", + if (wcscmp(wp, L"-32") != 0) + continue; # endif - PY_MAJOR_VERSION, PY_MINOR_VERSION); - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, subkey, 0, KEY_QUERY_VALUE, &hKey) - != ERROR_SUCCESS) + + if (major != PY_MAJOR_VERSION) + continue; +# ifdef USE_LIMITED_API + // Search the latest version. + if ((minor > maxminor) + && (minor >= ((Py_LIMITED_API >> 16) & 0xff))) + { + maxminor = minor; + wcscpy(keyfound, keyname); + hKeyFound = hKeyTop[i]; + } +# else + // Check if it matches with the compiled version. + if (minor == PY_MINOR_VERSION) + { + wcscpy(keyfound, keyname); + hKeyFound = hKeyTop[i]; + break; + } +# endif + } + RegCloseKey(hKey); +# ifdef USE_LIMITED_API + if (hKeyFound != NULL) + break; +# endif + } + if (hKeyFound == NULL) return NULL; - rc = RegQueryValueA(hKey, NULL, installpath, &len); - RegCloseKey(hKey); - if (ERROR_SUCCESS != rc) + + swprintf(subkey, ARRAY_LENGTH(subkey), L"%ls\\%ls\\InstallPath", + pythoncore, keyfound); + ret = RegGetValueW(hKeyFound, subkey, NULL, RRF_RT_REG_SZ, + NULL, NULL, &len); + if (ret != ERROR_MORE_DATA && ret != ERROR_SUCCESS) return NULL; - cp = installpath + len; - // Just in case registry value contains null terminators. - while (cp > installpath && *(cp-1) == '\0') - --cp; - // Remove trailing path separators. - while (cp > installpath && (*(cp-1) == '\\' || *(cp-1) == '/')) - --cp; - // Ignore if InstallPath is effectively empty. - if (cp <= installpath) + size_t len2 = len / sizeof(WCHAR) + 1 + strlen(libname); + WCHAR *path = alloc(len2 * sizeof(WCHAR)); + if (path == NULL) return NULL; - sysliblen = (cp - installpath) + 1 + STRLEN(libname) + 1; - syslibname = alloc(sysliblen); - vim_snprintf(syslibname, sysliblen, "%.*s\\%s", - (int)(cp - installpath), installpath, libname); - return syslibname; + ret = RegGetValueW(hKeyFound, subkey, NULL, RRF_RT_REG_SZ, + NULL, path, &len); + if (ret != ERROR_SUCCESS) + { + vim_free(path); + return NULL; + } + // Remove trailing path separators. + size_t len3 = wcslen(path); + if ((len3 > 0) && (path[len3 - 1] == L'/' || path[len3 - 1] == L'\\')) + --len3; + swprintf(path + len3, len2 - len3, L"\\%hs", libname); + return path; } # endif @@ -923,11 +982,13 @@ py3_runtime_link_init(char *libname, int verbose) if (!hinstPy3) { // Attempt to use the path from InstallPath as stored in the registry. - char *syslibname = py3_get_system_libname(libname); + WCHAR *syslibname = py3_get_system_libname(libname); if (syslibname != NULL) { - hinstPy3 = load_dll(syslibname); + hinstPy3 = LoadLibraryExW(syslibname, NULL, + LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | + LOAD_LIBRARY_SEARCH_SYSTEM32); vim_free(syslibname); } } diff --git a/src/version.c b/src/version.c index 73a573df58..111254eade 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2026, /**/ 2025, /**/ From de3295dd0c68a8d7540a751a99ee056fd5b9a7a4 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sun, 15 Oct 2023 09:44:50 +0200 Subject: [PATCH 41/47] patch 9.0.2027: Vim9: no support for bitwise operators in lambda funcs Problem: Vim9: no support for bitwise operators in lambda funcs Solution: move "evaluate" assignment a bit up in order to decide to perform bitwise operations closes: #13342 closes: #13345 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan --- src/eval.c | 28 ++++++++++++------------- src/testdir/test_expr.vim | 44 +++++++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/eval.c b/src/eval.c index 46eec35572..34502f965b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3515,7 +3515,8 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) return OK; // Handle a bitwise left or right shift operator - if (rettv->v_type != VAR_NUMBER) + evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); + if (evaluate && rettv->v_type != VAR_NUMBER) { // left operand should be a number emsg(_(e_bitshift_ops_must_be_number)); @@ -3523,7 +3524,6 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) return FAIL; } - evaluate = evalarg == NULL ? 0 : (evalarg->eval_flags & EVAL_EVALUATE); vim9script = in_vim9script(); if (getnext) { @@ -3553,20 +3553,20 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg) return FAIL; } - if (var2.v_type != VAR_NUMBER || var2.vval.v_number < 0) - { - // right operand should be a positive number - if (var2.v_type != VAR_NUMBER) - emsg(_(e_bitshift_ops_must_be_number)); - else - emsg(_(e_bitshift_ops_must_be_positive)); - clear_tv(rettv); - clear_tv(&var2); - return FAIL; - } - if (evaluate) { + if (var2.v_type != VAR_NUMBER || var2.vval.v_number < 0) + { + // right operand should be a positive number + if (var2.v_type != VAR_NUMBER) + emsg(_(e_bitshift_ops_must_be_number)); + else + emsg(_(e_bitshift_ops_must_be_positive)); + clear_tv(rettv); + clear_tv(&var2); + return FAIL; + } + if (var2.vval.v_number > MAX_LSHIFT_BITS) // shifting more bits than we have always results in zero rettv->vval.v_number = 0; diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim index 40b7809d15..d94ba4b58c 100644 --- a/src/testdir/test_expr.vim +++ b/src/testdir/test_expr.vim @@ -1041,6 +1041,50 @@ func Test_bitwise_shift() assert_equal(16, a) END call v9.CheckDefAndScriptSuccess(lines) + + let lines =<< trim END + # Use in a lambda function + const DivBy2Ref_A = (n: number): number => n >> 1 + assert_equal(16, DivBy2Ref_A(32)) + const DivBy2Ref_B = (n: number): number => (n) >> 1 + assert_equal(16, DivBy2Ref_B(32)) + const MultBy2Ref_A = (n: number): number => n << 1 + assert_equal(8, MultBy2Ref_A(4)) + const MultBy2Ref_B = (n: number): number => (n) << 1 + assert_equal(8, MultBy2Ref_B(4)) + + def DivBy2_A(): func(number): number + return (n: number): number => n >> 1 + enddef + assert_equal(16, DivBy2_A()(32)) + def DivBy2_B(): func(number): number + return (n: number): number => (n) >> 1 + enddef + assert_equal(16, DivBy2_B()(32)) + def MultBy2_A(): func(number): number + return (n: number): number => n << 1 + enddef + assert_equal(64, MultBy2_A()(32)) + def MultBy2_B(): func(number): number + return (n: number): number => (n) << 1 + enddef + assert_equal(64, MultBy2_B()(32)) + END + call v9.CheckDefAndScriptSuccess(lines) + + " Use in a legacy lambda function + const DivBy2Ref_A = {n -> n >> 1} + call assert_equal(16, DivBy2Ref_A(32)) + func DivBy2_A() + return {n -> n >> 1} + endfunc + call assert_equal(16, DivBy2_A()(32)) + const MultBy2Ref_A = {n -> n << 1} + call assert_equal(64, MultBy2Ref_A(32)) + func MultBy2_A() + return {n -> n << 1} + endfunc + call assert_equal(64, MultBy2_A()(32)) endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 111254eade..33d4bf3ef6 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2027, /**/ 2026, /**/ From 5d03525cdef5db1b1cedfa26c6f8a21aaa207ec0 Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Sun, 15 Oct 2023 09:50:53 +0200 Subject: [PATCH 42/47] patch 9.0.2028: confusing build dependencies Problem: confusing build dependencies Solution: clean them up, make them parallelizable Separate vim binary and unittest dependencies, make them parallelizable Clean up make dependencies so Vim and unit test binaries only depend on the object files they need. This fixes an existing issue where after running unit tests, the Vim binary would be invalidated, which results in it having to be linked again when running script tests, even though Vim was already previously built. Make link.sh (script we use to link those binaries) generate namespaced temporary files for each app to avoid them colliding with each other. This allows `unittesttargets` to be built in parallel. These fixes are useful when using link-time-optimization as the link phase could now take minutes rather than a few seconds. closes: #13344 Signed-off-by: Christian Brabandt Co-authored-by: Yee Cheng Chin --- src/Makefile | 23 +++++++++++--------- src/link.sh | 59 ++++++++++++++++++++++++++------------------------- src/version.c | 2 ++ 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/src/Makefile b/src/Makefile index 4d0a5ebad5..c8a84065f5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2053,12 +2053,12 @@ CCC = $(CCC_NF) $(ALL_CFLAGS) # Link the target for normal use or debugging. # A shell script is used to try linking without unnecessary libraries. -$(VIMTARGET): auto/config.mk objects $(OBJ) version.c version.h - $(CCC) version.c -o objects/version.o +$(VIMTARGET): auto/config.mk $(OBJ) objects/version.o @$(BUILD_DATE_MSG) @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \ -o $(VIMTARGET) $(OBJ) $(ALL_LIBS)" \ MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \ + PROG="vim" \ sh $(srcdir)/link.sh xxd/xxd$(EXEEXT): xxd/xxd.c @@ -2267,32 +2267,32 @@ testclean: # Unittests # It's build just like Vim to satisfy all dependencies. -$(JSON_TEST_TARGET): auto/config.mk objects $(JSON_TEST_OBJ) - $(CCC) version.c -o objects/version.o +$(JSON_TEST_TARGET): auto/config.mk $(JSON_TEST_OBJ) objects/version.o @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \ -o $(JSON_TEST_TARGET) $(JSON_TEST_OBJ) $(ALL_LIBS)" \ MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \ + PROG="json_test" \ sh $(srcdir)/link.sh -$(KWORD_TEST_TARGET): auto/config.mk objects $(KWORD_TEST_OBJ) - $(CCC) version.c -o objects/version.o +$(KWORD_TEST_TARGET): auto/config.mk $(KWORD_TEST_OBJ) objects/version.o @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \ -o $(KWORD_TEST_TARGET) $(KWORD_TEST_OBJ) $(ALL_LIBS)" \ MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \ + PROG="kword_test" \ sh $(srcdir)/link.sh -$(MEMFILE_TEST_TARGET): auto/config.mk objects $(MEMFILE_TEST_OBJ) - $(CCC) version.c -o objects/version.o +$(MEMFILE_TEST_TARGET): auto/config.mk $(MEMFILE_TEST_OBJ) objects/version.o @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \ -o $(MEMFILE_TEST_TARGET) $(MEMFILE_TEST_OBJ) $(ALL_LIBS)" \ MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \ + PROG="memfile_test" \ sh $(srcdir)/link.sh -$(MESSAGE_TEST_TARGET): auto/config.mk objects $(MESSAGE_TEST_OBJ) - $(CCC) version.c -o objects/version.o +$(MESSAGE_TEST_TARGET): auto/config.mk $(MESSAGE_TEST_OBJ) objects/version.o @LINK="$(PURIFY) $(SHRPENV) $(CClink) $(ALL_LIB_DIRS) $(LDFLAGS) \ -o $(MESSAGE_TEST_TARGET) $(MESSAGE_TEST_OBJ) $(ALL_LIBS)" \ MAKE="$(MAKE)" LINK_AS_NEEDED=$(LINK_AS_NEEDED) \ + PROG="message_test" \ sh $(srcdir)/link.sh # install targets @@ -3503,6 +3503,9 @@ objects/usercmd.o: usercmd.c objects/userfunc.o: userfunc.c $(CCC) -o $@ userfunc.c +objects/version.o: version.c + $(CCC) -o $@ version.c + objects/vim9class.o: vim9class.c $(CCC) -o $@ vim9class.c diff --git a/src/link.sh b/src/link.sh index 71ee061e31..e4030de861 100755 --- a/src/link.sh +++ b/src/link.sh @@ -13,13 +13,13 @@ # Otherwise this script is fail-safe, falling back to the original full link # command if anything fails. -echo "$LINK " >link.cmd +echo "$LINK " >link_$PROG.cmd exit_value=0 if test "$LINK_AS_NEEDED" = yes; then echo "link.sh: \$LINK_AS_NEEDED set to 'yes': invoking linker directly." - cat link.cmd - if sh link.cmd; then + cat link_$PROG.cmd + if sh link_$PROG.cmd; then exit_value=0 echo "link.sh: Linked fine" else @@ -49,53 +49,53 @@ else # - Don't remove the last -lm: On HP-UX Vim links OK but crashes when the GTK # GUI is started, because the "floor" symbol could not be resolved. # - cat link.cmd - if sh link.cmd; then + cat link_$PROG.cmd + if sh link_$PROG.cmd; then touch auto/link.sed - cp link.cmd linkit.sh + cp link_$PROG.cmd linkit_$PROG.sh for libname in SM ICE nsl dnet dnet_stub inet socket dir elf iconv Xt Xmu Xp Xpm X11 Xdmcp x w perl dl pthread thread readline m crypt attr; do cont=yes while test -n "$cont"; do - if grep "l$libname " linkit.sh >/dev/null; then - if test ! -f link1.sed; then + if grep "l$libname " linkit_$PROG.sh >/dev/null; then + if test ! -f link1_$PROG.sed; then echo "link.sh: OK, linking works, let's try omitting a few libraries." echo "link.sh: See auto/link.log for details." rm -f auto/link.log fi - echo "s/-l$libname *//" >link1.sed - sed -f auto/link.sed linkit2.sh - sed -f link1.sed linkit.sh + echo "s/-l$libname *//" >link1_$PROG.sed + sed -f auto/link.sed linkit2_$PROG.sh + sed -f link1_$PROG.sed linkit_$PROG.sh # keep the last -lm - if test $libname != "m" || grep "lm " linkit.sh >/dev/null; then + if test $libname != "m" || grep "lm " linkit_$PROG.sh >/dev/null; then echo "link.sh: Trying to omit the $libname library..." - cat linkit.sh >>auto/link.log + cat linkit_$PROG.sh >>auto/link.log # Redirect this link output, it may contain error messages which # should be ignored. - if sh linkit.sh >>auto/link.log 2>&1; then + if sh linkit_$PROG.sh >>auto/link.log 2>&1; then echo "link.sh: Vim doesn't need the $libname library!" - cat link1.sed >>auto/link.sed + cat link1_$PROG.sed >>auto/link.sed rm -f auto/pathdef.c else echo "link.sh: Vim DOES need the $libname library." cont= - cp link.cmd linkit.sh + cp link_$PROG.cmd linkit_$PROG.sh fi else cont= - cp link.cmd linkit.sh + cp link_$PROG.cmd linkit_$PROG.sh fi else cont= - cp link.cmd linkit.sh + cp link_$PROG.cmd linkit_$PROG.sh fi done done if test ! -f auto/pathdef.c; then $MAKE objects/pathdef.o fi - if test ! -f link1.sed; then + if test ! -f link1_$PROG.sed; then echo "link.sh: Linked fine, no libraries can be omitted" - touch link3.sed + touch link3_$PROG.sed fi else exit_value=$? @@ -107,29 +107,29 @@ fi # if test -s auto/link.sed; then echo "link.sh: Using auto/link.sed file to omit a few libraries" - sed -f auto/link.sed linkit.sh - cat linkit.sh - if sh linkit.sh; then + sed -f auto/link.sed linkit_$PROG.sh + cat linkit_$PROG.sh + if sh linkit_$PROG.sh; then exit_value=0 echo "link.sh: Linked fine with a few libraries omitted" else exit_value=$? echo "link.sh: Linking failed, making auto/link.sed empty and trying again" - mv -f auto/link.sed link2.sed + mv -f auto/link.sed link2_$PROG.sed touch auto/link.sed rm -f auto/pathdef.c $MAKE objects/pathdef.o fi fi -if test -f auto/link.sed -a ! -s auto/link.sed -a ! -f link3.sed; then +if test -f auto/link.sed -a ! -s auto/link.sed -a ! -f link3_$PROG.sed; then echo "link.sh: Using unmodified link command" - cat link.cmd - if sh link.cmd; then + cat link_$PROG.cmd + if sh link_$PROG.cmd; then exit_value=0 echo "link.sh: Linked OK" else exit_value=$? - if test -f link2.sed; then + if test -f link2_$PROG.sed; then echo "link.sh: Linking doesn't work at all, removing auto/link.sed" rm -f auto/link.sed fi @@ -141,7 +141,8 @@ fi # # cleanup # -rm -f link.cmd linkit.sh link1.sed link2.sed link3.sed linkit2.sh +rm -f link_$PROG.cmd linkit_$PROG.sh link1_$PROG.sed link2_$PROG.sed \ + link3_$PROG.sed linkit2_$PROG.sh # # return an error code if something went wrong diff --git a/src/version.c b/src/version.c index 33d4bf3ef6..997acf39dc 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2028, /**/ 2027, /**/ From 1ace49fb98fa93e2fcff421a5f7da1aa41c512ed Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sun, 15 Oct 2023 09:53:41 +0200 Subject: [PATCH 43/47] patch 9.0.2029: Vim9: no support for partials using call() Problem: Vim9: no support for partials using call() Solution: Add support closes: #13341 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan --- src/eval.c | 6 +++ src/testdir/test_vim9_class.vim | 69 +++++++++++++++++++++++++++++++++ src/userfunc.c | 12 ++++++ src/version.c | 2 + 4 files changed, 89 insertions(+) diff --git a/src/eval.c b/src/eval.c index 34502f965b..e888fecc8a 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2551,6 +2551,12 @@ eval_func( funcexe.fe_lastline = curwin->w_cursor.lnum; funcexe.fe_evaluate = evaluate; funcexe.fe_partial = partial; + if (partial != NULL) + { + funcexe.fe_object = partial->pt_obj; + if (funcexe.fe_object != NULL) + ++funcexe.fe_object->obj_refcount; + } funcexe.fe_basetv = basetv; funcexe.fe_check_type = type; funcexe.fe_found_var = found_var; diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 3af9a1f87a..b9f2910205 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -7510,6 +7510,21 @@ def Test_object_funcref() END v9.CheckSourceSuccess(lines) + # Using object method funcref at the script level + lines =<< trim END + vim9script + class A + this.val: number + def Foo(): number + return this.val + enddef + endclass + var a = A.new(345) + var Fn = a.Foo + assert_equal(345, Fn()) + END + v9.CheckSourceSuccess(lines) + # Using object method funcref from another object method lines =<< trim END vim9script @@ -7604,6 +7619,26 @@ def Test_object_funcref() a.Bar() END v9.CheckSourceSuccess(lines) + + # Using object method funcref using call() + lines =<< trim END + vim9script + class A + this.val: number + def Foo(): number + return this.val + enddef + endclass + + def Bar(obj: A) + assert_equal(123, call(obj.Foo, [])) + enddef + + var a = A.new(123) + Bar(a) + assert_equal(123, call(a.Foo, [])) + END + v9.CheckSourceSuccess(lines) enddef " Test for using a class method as a funcref @@ -7637,6 +7672,21 @@ def Test_class_funcref() END v9.CheckSourceSuccess(lines) + # Using class method funcref at the script level + lines =<< trim END + vim9script + class A + public static val: number + static def Foo(): number + return val + enddef + endclass + A.val = 567 + var Fn = A.Foo + assert_equal(567, Fn()) + END + v9.CheckSourceSuccess(lines) + # Using function() to get a class method funcref lines =<< trim END vim9script @@ -7725,6 +7775,25 @@ def Test_class_funcref() A.Bar() END v9.CheckSourceSuccess(lines) + + # Using class method funcref using call() + lines =<< trim END + vim9script + class A + public static val: number + static def Foo(): number + return val + enddef + endclass + + def Bar() + A.val = 468 + assert_equal(468, call(A.Foo, [])) + enddef + Bar() + assert_equal(468, call(A.Foo, [])) + END + v9.CheckSourceSuccess(lines) enddef " Test for using an object member as a funcref diff --git a/src/userfunc.c b/src/userfunc.c index 0f487fc120..db16b68049 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3540,6 +3540,12 @@ func_call( funcexe.fe_lastline = curwin->w_cursor.lnum; funcexe.fe_evaluate = TRUE; funcexe.fe_partial = partial; + if (partial != NULL) + { + funcexe.fe_object = partial->pt_obj; + if (funcexe.fe_object != NULL) + ++funcexe.fe_object->obj_refcount; + } funcexe.fe_selfdict = selfdict; r = call_func(name, -1, rettv, argc, argv, &funcexe); } @@ -3580,6 +3586,12 @@ call_callback( CLEAR_FIELD(funcexe); funcexe.fe_evaluate = TRUE; funcexe.fe_partial = callback->cb_partial; + if (callback->cb_partial != NULL) + { + funcexe.fe_object = callback->cb_partial->pt_obj; + if (funcexe.fe_object != NULL) + ++funcexe.fe_object->obj_refcount; + } ++callback_depth; ret = call_func(callback->cb_name, len, rettv, argcount, argvars, &funcexe); --callback_depth; diff --git a/src/version.c b/src/version.c index 997acf39dc..ec9a07d074 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2029, /**/ 2028, /**/ From 47510f3d6598a1218958c03ed11337a43b73f48d Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 15 Oct 2023 09:56:16 +0200 Subject: [PATCH 44/47] patch 9.0.2030: no max callback recursion limit Problem: no max callback recursion limit Solution: bail out, if max call recursion for callback functions has been reached. This checks the 'maxfuncdepth' setting and throws E169 when a callback function recursively calls itself. closes: #13337 closes: #13339 Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 3 ++- src/testdir/test_popupwin.vim | 6 ++++++ src/userfunc.c | 7 +++++++ src/version.c | 2 ++ 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 502e77ba1f..492c7da1ee 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.0. Last change: 2023 Aug 15 +*options.txt* For Vim version 9.0. Last change: 2023 Oct 14 VIM REFERENCE MANUAL by Bram Moolenaar @@ -5485,6 +5485,7 @@ A jump table for the options with a short description can be found at |Q_op|. Increasing this limit above 200 also changes the maximum for Ex command recursion, see |E169|. See also |:function|. + Also used for maximum depth of callback functions. *'maxmapdepth'* *'mmd'* *E223* 'maxmapdepth' 'mmd' number (default 1000) diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 60dc5d9b99..a256ddfb2e 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -4205,5 +4205,11 @@ func Test_popupwin_with_error() call StopVimInTerminal(buf) endfunc +func Test_popup_close_callback_recursive() + " this invokes the callback recursively + let winid = popup_create('something', #{callback: 'popup_close'}) + redraw + call assert_fails('call popup_close(winid)', 'E169') +endfunc " vim: shiftwidth=2 sts=2 diff --git a/src/userfunc.c b/src/userfunc.c index db16b68049..a3b8bdc103 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3583,6 +3583,13 @@ call_callback( if (callback->cb_name == NULL || *callback->cb_name == NUL) return FAIL; + + if (callback_depth > p_mfd) + { + emsg(_(e_command_too_recursive)); + return FAIL; + } + CLEAR_FIELD(funcexe); funcexe.fe_evaluate = TRUE; funcexe.fe_partial = callback->cb_partial; diff --git a/src/version.c b/src/version.c index ec9a07d074..b90f76fc15 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2030, /**/ 2029, /**/ From d7ae263af8f6a2da55ce3702d18c53ab1418bca7 Mon Sep 17 00:00:00 2001 From: Evgeni Chasnovski Date: Sun, 15 Oct 2023 09:59:00 +0200 Subject: [PATCH 45/47] patch 9.0.2031: TextChangedI may be triggered by non-insert mode change Problem: `TextChangedI` can trigger on entering Insert mode if there was previously a change not in Insert mode. Solution: Make it trigger only when text is actually changed in Insert mode. closes: #13265 closes: #13338 Signed-off-by: Christian Brabandt Co-authored-by: Evgeni Chasnovski --- src/edit.c | 4 ++++ src/testdir/test_autocmd.vim | 23 +++++++++++++++++------ src/version.c | 2 ++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/edit.c b/src/edit.c index a515425253..014d1c7ed3 100644 --- a/src/edit.c +++ b/src/edit.c @@ -174,6 +174,9 @@ edit( return FALSE; } ins_compl_clear(); // clear stuff for CTRL-X mode + // Reset Changedtick_i, so that TextChangedI will only be triggered for stuff + // from insert mode + curbuf->b_last_changedtick_i = CHANGEDTICK(curbuf); /* * Trigger InsertEnter autocommands. Do not do this for "r" or "grx". @@ -840,6 +843,7 @@ edit( if (cmdchar != 'r' && cmdchar != 'v' && c != Ctrl_C) ins_apply_autocmds(EVENT_INSERTLEAVE); did_cursorhold = FALSE; + curbuf->b_last_changedtick = CHANGEDTICK(curbuf); return (c == Ctrl_O); } continue; diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 419005aa6e..1e1bb4540a 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -2566,27 +2566,28 @@ func Test_ChangedP() call cursor(3, 1) let g:autocmd = '' call feedkeys("o\", 'tnix') - call assert_equal('I', g:autocmd) + " `TextChangedI` triggers only if text is actually changed in Insert mode + call assert_equal('', g:autocmd) let g:autocmd = '' call feedkeys("Sf", 'tnix') - call assert_equal('II', g:autocmd) + call assert_equal('I', g:autocmd) let g:autocmd = '' call feedkeys("Sf\", 'tnix') - call assert_equal('IIP', g:autocmd) + call assert_equal('IP', g:autocmd) let g:autocmd = '' call feedkeys("Sf\\", 'tnix') - call assert_equal('IIPP', g:autocmd) + call assert_equal('IPP', g:autocmd) let g:autocmd = '' call feedkeys("Sf\\\", 'tnix') - call assert_equal('IIPPP', g:autocmd) + call assert_equal('IPPP', g:autocmd) let g:autocmd = '' call feedkeys("Sf\\\\", 'tnix') - call assert_equal('IIPPPP', g:autocmd) + call assert_equal('IPPPP', g:autocmd) call assert_equal(['foo', 'bar', 'foobar', 'foo'], getline(1, '$')) " TODO: how should it handle completeopt=noinsert,noselect? @@ -3610,6 +3611,16 @@ func Test_Changed_ChangedI() " call assert_equal('N4', g:autocmd_n) call assert_equal('I3', g:autocmd_i) + " TextChangedI should only trigger if change was done in Insert mode + let g:autocmd_i = '' + call feedkeys("yypi\", 'tnix') + call assert_equal('', g:autocmd_i) + + " TextChanged should only trigger if change was done in Normal mode + let g:autocmd_n = '' + call feedkeys("ibar\", 'tnix') + call assert_equal('', g:autocmd_n) + " CleanUp call test_override("char_avail", 0) au! TextChanged diff --git a/src/version.c b/src/version.c index b90f76fc15..4bac9840f3 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2031, /**/ 2030, /**/ From 3c81f47a0584e7915217397fed0488091a62df82 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 15 Oct 2023 16:02:08 +0800 Subject: [PATCH 46/47] runtime(doc): correct / behavior in 'wildmenu' (#13336) "but don't insert it" is wrong. Also move them just below as they are more similar. --- runtime/doc/index.txt | 2 +- runtime/doc/options.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index e7e99e2ec0..a9a8eec257 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1117,7 +1117,7 @@ commands in wildmenu mode (see 'wildmenu') move up to parent / select the previous match move down to submenu / select the next match select the previous match / move up to parent - select the next match / move down to submenu + select the next match / move down to submenu move into submenu when doing menu completion other stop completion and insert the typed character diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 492c7da1ee..6f38bc02be 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -9333,12 +9333,12 @@ A jump table for the options with a short description can be found at |Q_op|. When using the popup menu for command line completion, the following keys have special meanings: - select previous/next match (like CTRL-P/CTRL-N) + - select a match several entries back + - select a match several entries further - 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 a match several entries back, but don't insert it. - - Select a match several entries further, but don't insert it. CTRL-E - end completion, go back to what was there before selecting a match. CTRL-Y - accept the currently selected match and stop From f5a94d5165bb9e390797da50a1fa7a87df3fbee4 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 15 Oct 2023 10:03:30 +0200 Subject: [PATCH 47/47] patch 9.0.2032: cannot get mouse click pos for tab or virt text Problem: Cannot accurately get mouse clicking position when clicking on a TAB or with virtual text. Solution: Add a "coladd" field to getmousepos() result. closes: #13335 Signed-off-by: Christian Brabandt Co-authored-by: zeertzjq --- runtime/doc/builtin.txt | 2 ++ src/beval.c | 2 +- src/mouse.c | 12 +++++++----- src/move.c | 2 +- src/proto/mouse.pro | 2 +- src/testdir/test_functions.vim | 31 +++++++++++++++++++++++++++++++ src/testdir/test_popupwin.vim | 12 ++++++------ src/version.c | 2 ++ 8 files changed, 51 insertions(+), 14 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index f621fc0516..a9bba86d8c 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -3928,6 +3928,8 @@ getmousepos() *getmousepos()* wincol column inside "winid" line text line inside "winid" column text column inside "winid" + coladd offset (in screen columns) from the + start of the clicked char All numbers are 1-based. If not over a window, e.g. when in the command line, then only diff --git a/src/beval.c b/src/beval.c index cd91863837..65dfbc2aad 100644 --- a/src/beval.c +++ b/src/beval.c @@ -74,7 +74,7 @@ find_word_under_cursor( } } - col = vcol2col(wp, lnum, col); + col = vcol2col(wp, lnum, col, NULL); scol = col; if (VIsual_active diff --git a/src/mouse.c b/src/mouse.c index fe5c14cac5..f895779ac5 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -172,9 +172,7 @@ get_fpos_of_mouse(pos_T *mpos) if (mouse_comp_pos(curwin, &row, &col, &mpos->lnum, NULL)) return IN_STATUS_LINE; // past bottom - mpos->col = vcol2col(wp, mpos->lnum, col); - - mpos->coladd = 0; + mpos->col = vcol2col(wp, mpos->lnum, col, &mpos->coladd); return IN_BUFFER; } #endif @@ -3204,7 +3202,7 @@ mouse_find_win(int *rowp, int *colp, mouse_find_T popup UNUSED) * The first column is zero. */ int -vcol2col(win_T *wp, linenr_T lnum, int vcol) +vcol2col(win_T *wp, linenr_T lnum, int vcol, colnr_T *coladdp) { char_u *line; chartabsize_T cts; @@ -3222,6 +3220,8 @@ vcol2col(win_T *wp, linenr_T lnum, int vcol) } clear_chartabsize_arg(&cts); + if (coladdp != NULL) + *coladdp = vcol - cts.cts_vcol; return (int)(cts.cts_ptr - line); } #endif @@ -3242,6 +3242,7 @@ f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv) varnumber_T wincol = 0; linenr_T lnum = 0; varnumber_T column = 0; + colnr_T coladd = 0; if (rettv_dict_alloc(rettv) == FAIL) return; @@ -3275,7 +3276,7 @@ f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv) if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) { (void)mouse_comp_pos(wp, &row, &col, &lnum, NULL); - col = vcol2col(wp, lnum, col); + col = vcol2col(wp, lnum, col, &coladd); column = col + 1; } } @@ -3285,5 +3286,6 @@ f_getmousepos(typval_T *argvars UNUSED, typval_T *rettv) dict_add_number(d, "wincol", wincol); dict_add_number(d, "line", (varnumber_T)lnum); dict_add_number(d, "column", column); + dict_add_number(d, "coladd", coladd); } #endif diff --git a/src/move.c b/src/move.c index 72490d2f85..bb6502467e 100644 --- a/src/move.c +++ b/src/move.c @@ -1547,7 +1547,7 @@ f_screenpos(typval_T *argvars UNUSED, typval_T *rettv) static int virtcol2col(win_T *wp, linenr_T lnum, int vcol) { - int offset = vcol2col(wp, lnum, vcol - 1); + int offset = vcol2col(wp, lnum, vcol - 1, NULL); char_u *line = ml_get_buf(wp->w_buffer, lnum, FALSE); char_u *p = line + offset; diff --git a/src/proto/mouse.pro b/src/proto/mouse.pro index 2127d82436..ead8184985 100644 --- a/src/proto/mouse.pro +++ b/src/proto/mouse.pro @@ -21,6 +21,6 @@ void reset_held_button(void); int check_termcode_mouse(char_u *tp, int *slen, char_u *key_name, char_u *modifiers_start, int idx, int *modifiers); int mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump, int *plines_cache); win_T *mouse_find_win(int *rowp, int *colp, mouse_find_T popup); -int vcol2col(win_T *wp, linenr_T lnum, int vcol); +int vcol2col(win_T *wp, linenr_T lnum, int vcol, colnr_T *coladdp); void f_getmousepos(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index 50b7fb23ca..d598966977 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -3365,6 +3365,7 @@ func Test_getmousepos() \ wincol: 1, \ line: 1, \ column: 1, + \ coladd: 0, \ }, getmousepos()) call test_setmouse(1, 2) call assert_equal(#{ @@ -3375,6 +3376,7 @@ func Test_getmousepos() \ wincol: 2, \ line: 1, \ column: 1, + \ coladd: 1, \ }, getmousepos()) call test_setmouse(1, 8) call assert_equal(#{ @@ -3385,6 +3387,7 @@ func Test_getmousepos() \ wincol: 8, \ line: 1, \ column: 1, + \ coladd: 7, \ }, getmousepos()) call test_setmouse(1, 9) call assert_equal(#{ @@ -3395,6 +3398,7 @@ func Test_getmousepos() \ wincol: 9, \ line: 1, \ column: 2, + \ coladd: 0, \ }, getmousepos()) call test_setmouse(1, 12) call assert_equal(#{ @@ -3405,6 +3409,7 @@ func Test_getmousepos() \ wincol: 12, \ line: 1, \ column: 2, + \ coladd: 3, \ }, getmousepos()) call test_setmouse(1, 25) call assert_equal(#{ @@ -3415,6 +3420,29 @@ func Test_getmousepos() \ wincol: 25, \ line: 1, \ column: 4, + \ coladd: 0, + \ }, getmousepos()) + call test_setmouse(1, 28) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 28, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 28, + \ line: 1, + \ column: 7, + \ coladd: 0, + \ }, getmousepos()) + call test_setmouse(1, 29) + call assert_equal(#{ + \ screenrow: 1, + \ screencol: 29, + \ winid: win_getid(), + \ winrow: 1, + \ wincol: 29, + \ line: 1, + \ column: 8, + \ coladd: 0, \ }, getmousepos()) call test_setmouse(1, 50) call assert_equal(#{ @@ -3425,6 +3453,7 @@ func Test_getmousepos() \ wincol: 50, \ line: 1, \ column: 8, + \ coladd: 21, \ }, getmousepos()) " If the mouse is positioned past the last buffer line, "line" and "column" @@ -3438,6 +3467,7 @@ func Test_getmousepos() \ wincol: 25, \ line: 1, \ column: 4, + \ coladd: 0, \ }, getmousepos()) call test_setmouse(2, 50) call assert_equal(#{ @@ -3448,6 +3478,7 @@ func Test_getmousepos() \ wincol: 50, \ line: 1, \ column: 8, + \ coladd: 21, \ }, getmousepos()) bwipe! endfunc diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index a256ddfb2e..d01eccc412 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -2671,21 +2671,21 @@ func Test_popupwin_filter_mouse() eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{ \ screenrow: a:row, screencol: a:col, \ winid: win_getid(), winrow: a:row, wincol: a:col, - \ line: a:row, column: a:col, + \ line: a:row, column: a:col, coladd: 0, \ }}) endfunc func AddItemInPopupBorder(tests, winid, row, col) eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{ \ screenrow: a:row, screencol: a:col, \ winid: a:winid, winrow: a:row - 1, wincol: a:col - 3, - \ line: 0, column: 0, + \ line: 0, column: 0, coladd: 0, \ }}) endfunc - func AddItemInPopupText(tests, winid, row, col, textline, textcol) + func AddItemInPopupText(tests, winid, row, col, textline, textcol, coladd = 0) eval a:tests->add(#{clickrow: a:row, clickcol: a:col, result: #{ \ screenrow: a:row, screencol: a:col, \ winid: a:winid, winrow: a:row - 1, wincol: a:col - 3, - \ line: a:textline, column: a:textcol, + \ line: a:textline, column: a:textcol, coladd: a:coladd, \ }}) endfunc @@ -2717,7 +2717,7 @@ func Test_popupwin_filter_mouse() call AddItemInPopupText(tests, winid, 4, 6, 1, 1) call AddItemInPopupText(tests, winid, 4, 10, 1, 5) call AddItemInPopupText(tests, winid, 4, 11, 1, 6) - call AddItemInPopupText(tests, winid, 4, 17, 1, 6) + call AddItemInPopupText(tests, winid, 4, 17, 1, 6, 6) " text "long line th" call AddItemInPopupText(tests, winid, 5, 6, 2, 1) call AddItemInPopupText(tests, winid, 5, 10, 2, 5) @@ -2730,7 +2730,7 @@ func Test_popupwin_filter_mouse() call AddItemInPopupText(tests, winid, 7, 6, 3, 1) call AddItemInPopupText(tests, winid, 7, 10, 3, 5) call AddItemInPopupText(tests, winid, 7, 11, 3, 6) - call AddItemInPopupText(tests, winid, 7, 17, 3, 6) + call AddItemInPopupText(tests, winid, 7, 17, 3, 6, 6) for item in tests call test_setmouse(item.clickrow, item.clickcol) diff --git a/src/version.c b/src/version.c index 4bac9840f3..2e14a90bba 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2032, /**/ 2031, /**/