diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 09debc5058..c2ad724db9 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -3034,6 +3034,8 @@ test_feedinput({string}) none add key sequence to input buffer test_garbagecollect_now() none free memory right now for testing test_garbagecollect_soon() none free memory soon for testing test_getvalue({string}) any get value of an internal variable +test_gui_mouse_event({button}, {row}, {col}, {repeated}, {mods}) + none add a mouse event to the input buffer test_ignore_error({expr}) none ignore a specific error test_null_blob() Blob null value for testing test_null_channel() Channel null value for testing @@ -5853,7 +5855,10 @@ getqflist([{what}]) *getqflist()* bufname() to get the name module module name lnum line number in the buffer (first line is 1) + end_lnum + end of line number if the item is multiline col column number (first column is 1) + end_col end of column number if the item has range vcol |TRUE|: "col" is visual column |FALSE|: "col" is byte index nr error number @@ -11983,6 +11988,7 @@ scrollbind Compiled with 'scrollbind' support. (always true) showcmd Compiled with 'showcmd' support. signs Compiled with |:sign| support. smartindent Compiled with 'smartindent' support. +sodium Compiled with libsodium for better crypt support sound Compiled with sound support, e.g. `sound_playevent()` spell Compiled with spell checking support |spell|. startuptime Compiled with |--startuptime| support. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e5bc709905..a4b68821c0 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2399,6 +2399,23 @@ A jump table for the options with a short description can be found at |Q_op|. you write the file the encrypted bytes will be different. The whole undo file is encrypted, not just the pieces of text. + *E1193* *E1194* *E1195* *E1196* + *E1197* *E1198* *E1199* *E1200* *E1201* + xchacha20 XChaCha20 Cipher with Poly1305 Message Authentication + Code. Medium strong till strong encryption. + Encryption is provided by the libsodium library, it + requires Vim to be built with |+sodium| + It adds a seed and a message authentication code (MAC) + to the file. This needs at least a Vim 8.2.3022 to + read the encrypted file. + Encryption of swap files is not supported, therefore + no swap file will be used when xchacha20 encryption is + enabled. + Encryption of undo files is not yet supported, + therefore no undo file will currently be written. + CURRENTLY EXPERIMENTAL: Files written with this method + might have to be read back with the same version of + Vim if the binary format changes later. You should use "blowfish2", also to re-encrypt older files. diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt index 6fd2d45887..db505e7efc 100644 --- a/runtime/doc/testing.txt +++ b/runtime/doc/testing.txt @@ -1,4 +1,4 @@ -*testing.txt* For Vim version 8.2. Last change: 2021 Apr 02 +*testing.txt* For Vim version 8.2. Last change: 2021 Jun 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -78,6 +78,30 @@ test_getvalue({name}) *test_getvalue()* Can also be used as a |method|: > GetName()->test_getvalue() +< + *test_gui_mouse_event()* +test_gui_mouse_event({button}, {row}, {col}, {multiclick}, {modifiers}) + Inject a mouse button click event. This function works only + when GUI is running. + The supported values for {button} are: + 0 right mouse button + 1 middle mouse button + 2 left mouse button + 3 mouse button release + 4 scroll wheel down + 5 scroll wheel up + 6 scroll wheel left + 7 scroll wheel right + {row} and {col} specify the location of the mouse click. + To inject a multiclick event, set {multiclick} to 1. + The supported values for {modifiers} are: + 4 shift is pressed + 8 alt is pressed + 16 ctrl is pressed + After injecting the mouse event you probably should call + |feedkeys()| to have them processed, e.g.: > + call feedkeys("y", 'Lx!') + test_ignore_error({expr}) *test_ignore_error()* Ignore any error containing {expr}. A normal message is given diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 09d5a39db7..de7f197078 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1021,6 +1021,7 @@ Testing: *test-functions* test_garbagecollect_now() free memory right now test_garbagecollect_soon() set a flag to free memory soon test_getvalue() get value of an internal variable + test_gui_mouse_event() add a GUI mouse event to the input buffer test_ignore_error() ignore a specific error message test_null_blob() return a null Blob test_null_channel() return a null Channel diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 5820d2e18e..6f85b41511 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -446,6 +446,7 @@ m *+ruby/dyn* Ruby interface |ruby-dynamic| |/dyn| T *+scrollbind* |'scrollbind'| B *+signs* |:sign| N *+smartindent* |'smartindent'| +B *+sodium* compiled with libsodium for better encryption support B *+sound* |sound_playevent()|, |sound_playfile()| functions, etc. N *+spell* spell checking support, see |spell| N *+startuptime* |--startuptime| argument diff --git a/src/INSTALLpc.txt b/src/INSTALLpc.txt index b5c1a0b58c..d5516c891a 100644 --- a/src/INSTALLpc.txt +++ b/src/INSTALLpc.txt @@ -322,6 +322,9 @@ MSYS2 has its own git package, and you can also install it via pacman: $ pacman -S git +For enabling libsodium support, you also need to install the package + + $ pacman -S mingw-w64-x86_64-libsodium 2.3. Keep the build environment up-to-date diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index c33200b33e..e93165dbf4 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -41,6 +41,9 @@ DEBUG=no # set to yes to measure code coverage COVERAGE=no +# better encryption support using libsodium +#SODIUM=yes + # set to SIZE for size, SPEED for speed, MAXSPEED for maximum optimization OPTIMIZE=MAXSPEED @@ -517,6 +520,10 @@ CXXFLAGS = -std=gnu++11 WINDRES_FLAGS = EXTRA_LIBS = +ifdef SODIUM +DEFINES += -DHAVE_SODIUM +endif + ifdef GETTEXT DEFINES += -DHAVE_GETTEXT -DHAVE_LOCALE_H GETTEXTINCLUDE = $(GETTEXT)/include @@ -660,6 +667,10 @@ DEFINES += -DFEAT_DIRECTX_COLOR_EMOJI endif endif +ifeq ($(SODIUM),yes) +SODIUMLIB = -lsodium +endif + # Only allow XPM for a GUI build. ifeq (yes, $(GUI)) @@ -1064,7 +1075,7 @@ $(EXEOBJC): | $(OUTDIR) ifeq ($(VIMDLL),yes) $(TARGET): $(OBJ) - $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid -lgdi32 $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) + $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid -lgdi32 $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) $(SODIUMLIB) $(GVIMEXE): $(EXEOBJG) $(VIMDLLBASE).dll $(CC) -L. $(EXELFLAGS) -mwindows -o $@ $(EXEOBJG) -l$(VIMDLLBASE) @@ -1073,7 +1084,7 @@ $(VIMEXE): $(EXEOBJC) $(VIMDLLBASE).dll $(CC) -L. $(EXELFLAGS) -o $@ $(EXEOBJC) -l$(VIMDLLBASE) else $(TARGET): $(OBJ) - $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) + $(LINK) $(CFLAGS) $(LFLAGS) -o $@ $(OBJ) $(LIB) -lole32 -luuid $(LUA_LIB) $(MZSCHEME_LIBDIR) $(MZSCHEME_LIB) $(PYTHONLIB) $(PYTHON3LIB) $(RUBYLIB) $(SODIUMLIB) endif upx: exes diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 42b6f8ddf6..4d6e49b377 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -41,6 +41,13 @@ # # Sound support: SOUND=yes (default is yes) # +# Sodium support: SODIUM=[Path to Sodium directory] +# Dynamic built with libsodium +# You need to install the msvc package from +# https://download.libsodium.org/libsodium/releases/ +# and package the libsodium.dll with Vim +# +# # DLL support (EXPERIMENTAL): VIMDLL=yes (default is no) # Creates vim{32,64}.dll, and stub gvim.exe and vim.exe. # The shared codes between the GUI and the console are built into @@ -372,6 +379,26 @@ SOUND = no ! endif !endif +!ifndef SODIUM +SODIUM = no +!endif + +!if "$(SODIUM)" != "no" +! if "$(CPU)" == "AMD64" +SOD_LIB = $(SODIUM)\x64\Release\v140\dynamic +! elseif "$(CPU)" == "i386" +SOD_LIB = $(SODIUM)\Win32\Release\v140\dynamic +! else +SODIUM = no +! endif +!endif + +!if "$(SODIUM)" != "no" +SOD_INC = /I "$(SODIUM)\include" +SOD_DEFS = -DFEAT_SODIUM +SOD_LIB = $(SOD_LIB)\libsodium.lib +!endif + !ifndef NETBEANS NETBEANS = $(GUI) !endif @@ -491,7 +518,7 @@ CON_LIB = $(CON_LIB) /DELAYLOAD:comdlg32.dll /DELAYLOAD:ole32.dll DelayImp.lib CFLAGS = -c /W3 /GF /nologo $(CVARS) -I. -Iproto -DHAVE_PATHDEF -DWIN32 \ $(CSCOPE_DEFS) $(TERM_DEFS) $(SOUND_DEFS) $(NETBEANS_DEFS) $(CHANNEL_DEFS) \ - $(NBDEBUG_DEFS) $(XPM_DEFS) \ + $(NBDEBUG_DEFS) $(XPM_DEFS) $(SOD_DEFS) $(SOD_INC) \ $(DEFINES) -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) #>>>>> end of choices @@ -1282,7 +1309,7 @@ conflags = $(conflags) /map /mapinfo:lines LINKARGS1 = $(linkdebug) $(conflags) LINKARGS2 = $(CON_LIB) $(GUI_LIB) $(NODEFAULTLIB) $(LIBC) $(OLE_LIB) user32.lib \ $(LUA_LIB) $(MZSCHEME_LIB) $(PERL_LIB) $(PYTHON_LIB) $(PYTHON3_LIB) $(RUBY_LIB) \ - $(TCL_LIB) $(SOUND_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(LINK_PDB) + $(TCL_LIB) $(SOUND_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(SOD_LIB) $(LINK_PDB) # Report link time code generation progress if used. !ifdef NODEBUG diff --git a/src/auto/configure b/src/auto/configure index 15b129caa6..ab451313f8 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -844,6 +844,7 @@ with_motif_lib with_tlib enable_largefile enable_canberra +enable_libsodium enable_acl enable_gpm enable_sysmouse @@ -1510,6 +1511,7 @@ Optional Features: --disable-desktop-database-update update disabled --disable-largefile omit support for large files --disable-canberra Do not use libcanberra. + --disable-libsodium Do not use libsodium. --disable-acl No check for ACL support. --disable-gpm Don't use gpm (Linux mouse daemon). --disable-sysmouse Don't use sysmouse (mouse in *BSD console). @@ -13235,6 +13237,70 @@ rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking --enable-libsodium argument" >&5 +$as_echo_n "checking --enable-libsodium argument... " >&6; } +# Check whether --enable-libsodium was given. +if test "${enable_libsodium+set}" = set; then : + enableval=$enable_libsodium; +else + enable_libsodium="maybe" +fi + + +if test "$enable_libsodium" = "maybe"; then + if test "$features" = "big" -o "$features" = "huge"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to yes" >&5 +$as_echo "Defaulting to yes" >&6; } + enable_libsodium="yes" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: Defaulting to no" >&5 +$as_echo "Defaulting to no" >&6; } + enable_libsodium="no" + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_libsodium" >&5 +$as_echo "$enable_libsodium" >&6; } +fi +if test "$enable_libsodium" = "yes"; then + if test "x$PKG_CONFIG" != "xno"; then + libsodium_lib=`$PKG_CONFIG --libs libsodium 2>/dev/null` + libsodium_cflags=`$PKG_CONFIG --cflags libsodium 2>/dev/null` + fi + if test "x$libsodium_lib" = "x"; then + libsodium_lib=-lsodium + libsodium_cflags= + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcanberra" >&5 +$as_echo_n "checking for libcanberra... " >&6; } + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $libsodium_cflags" + LIBS="$LIBS $libsodium_lib" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + # include + +int +main () +{ + + printf("%d", sodium_init()); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SODIUM 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no; try installing libsodium-dev" >&5 +$as_echo "no; try installing libsodium-dev" >&6; }; CFLAGS="$ac_save_CFLAGS"; LIBS="$ac_save_LIBS" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for st_blksize" >&5 $as_echo_n "checking for st_blksize... " >&6; } diff --git a/src/blowfish.c b/src/blowfish.c index 342bcc406e..4502a1c5db 100644 --- a/src/blowfish.c +++ b/src/blowfish.c @@ -596,7 +596,8 @@ crypt_blowfish_encode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last UNUSED) { bf_state_T *bfs = state->method_state; size_t i; @@ -619,7 +620,8 @@ crypt_blowfish_decode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last UNUSED) { bf_state_T *bfs = state->method_state; size_t i; @@ -680,5 +682,4 @@ blowfish_self_test(void) } return OK; } - #endif // FEAT_CRYPT diff --git a/src/bufwrite.c b/src/bufwrite.c index df8ab6eb57..e1cce5b5a2 100644 --- a/src/bufwrite.c +++ b/src/bufwrite.c @@ -30,6 +30,7 @@ struct bw_info int bw_flags; // FIO_ flags #ifdef FEAT_CRYPT buf_T *bw_buffer; // buffer being written + int bw_finish; // finish encrypting #endif char_u bw_rest[CONV_RESTLEN]; // not converted bytes int bw_restlen; // nr of bytes in bw_rest[] @@ -493,14 +494,14 @@ buf_write_bytes(struct bw_info *ip) if (crypt_works_inplace(ip->bw_buffer->b_cryptstate)) { # endif - crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len); + crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len, ip->bw_finish); # ifdef CRYPT_NOT_INPLACE } else { char_u *outbuf; - len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf); + len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf, ip->bw_finish); if (len == 0) return OK; // Crypt layer is buffering, will flush later. wlen = write_eintr(ip->bw_fd, outbuf, len); @@ -724,6 +725,7 @@ buf_write( #endif #ifdef FEAT_CRYPT write_info.bw_buffer = buf; + write_info.bw_finish = FALSE; #endif // After writing a file changedtick changes but we don't want to display @@ -2015,6 +2017,13 @@ restore_backup: ++s; if (++len != bufsize) continue; +#ifdef FEAT_CRYPT + if (write_info.bw_fd > 0 && lnum == end + && (write_info.bw_flags & FIO_ENCRYPTED) + && *buf->b_p_key != NUL && !filtering + && *ptr == NUL) + write_info.bw_finish = TRUE; + #endif if (buf_write_bytes(&write_info) == FAIL) { end = 0; // write error: break loop @@ -2118,6 +2127,12 @@ restore_backup: if (len > 0 && end > 0) { write_info.bw_len = len; +#ifdef FEAT_CRYPT + if (write_info.bw_fd > 0 && lnum >= end + && (write_info.bw_flags & FIO_ENCRYPTED) + && *buf->b_p_key != NUL && !filtering) + write_info.bw_finish = TRUE; + #endif if (buf_write_bytes(&write_info) == FAIL) end = 0; // write error nchars += len; diff --git a/src/config.h.in b/src/config.h.in index fbf4b2449c..0808cc3587 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -208,6 +208,7 @@ #undef HAVE_STRPTIME #undef HAVE_STRTOL #undef HAVE_CANBERRA +#undef HAVE_SODIUM #undef HAVE_ST_BLKSIZE #undef HAVE_SYSCONF #undef HAVE_SYSCTL diff --git a/src/configure.ac b/src/configure.ac index 0a6ff2304e..ea742a4c51 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -3952,6 +3952,43 @@ if test "$enable_canberra" = "yes"; then AC_MSG_RESULT(no; try installing libcanberra-dev); CFLAGS="$ac_save_CFLAGS"; LIBS="$ac_save_LIBS") fi +AC_MSG_CHECKING(--enable-libsodium argument) +AC_ARG_ENABLE(libsodium, + [ --disable-libsodium Do not use libsodium.], + , [enable_libsodium="maybe"]) + +if test "$enable_libsodium" = "maybe"; then + if test "$features" = "big" -o "$features" = "huge"; then + AC_MSG_RESULT(Defaulting to yes) + enable_libsodium="yes" + else + AC_MSG_RESULT(Defaulting to no) + enable_libsodium="no" + fi +else + AC_MSG_RESULT($enable_libsodium) +fi +if test "$enable_libsodium" = "yes"; then + if test "x$PKG_CONFIG" != "xno"; then + libsodium_lib=`$PKG_CONFIG --libs libsodium 2>/dev/null` + libsodium_cflags=`$PKG_CONFIG --cflags libsodium 2>/dev/null` + fi + if test "x$libsodium_lib" = "x"; then + libsodium_lib=-lsodium + libsodium_cflags= + fi + AC_MSG_CHECKING(for libcanberra) + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $libsodium_cflags" + LIBS="$LIBS $libsodium_lib" + AC_TRY_LINK([ + # include + ], [ + printf("%d", sodium_init()); ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SODIUM), + AC_MSG_RESULT(no; try installing libsodium-dev); CFLAGS="$ac_save_CFLAGS"; LIBS="$ac_save_LIBS") +fi dnl fstatfs() can take 2 to 4 arguments, try to use st_blksize if possible AC_MSG_CHECKING(for st_blksize) diff --git a/src/crypt.c b/src/crypt.c index 0164f1ce9d..1b2ece5db4 100644 --- a/src/crypt.c +++ b/src/crypt.c @@ -12,6 +12,10 @@ */ #include "vim.h" +#ifdef FEAT_SODIUM +# include +#endif + #if defined(FEAT_CRYPT) || defined(PROTO) /* * Optional encryption support. @@ -33,7 +37,7 @@ typedef struct { char *name; // encryption name as used in 'cryptmethod' char *magic; // magic bytes stored in file header int salt_len; // length of salt, or 0 when not using salt - int seed_len; // length of seed, or 0 when not using salt + int seed_len; // length of seed, or 0 when not using seed #ifdef CRYPT_NOT_INPLACE int works_inplace; // encryption/decryption can be done in-place #endif @@ -49,16 +53,16 @@ typedef struct { // Function pointers for encoding/decoding from one buffer into another. // Optional, however, these or the _buffer ones should be configured. void (*encode_fn)(cryptstate_T *state, char_u *from, size_t len, - char_u *to); + char_u *to, int last); void (*decode_fn)(cryptstate_T *state, char_u *from, size_t len, - char_u *to); + char_u *to, int last); // Function pointers for encoding and decoding, can buffer data if needed. // Optional (however, these or the above should be configured). long (*encode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, - char_u **newptr); + char_u **newptr, int last); long (*decode_buffer_fn)(cryptstate_T *state, char_u *from, size_t len, - char_u **newptr); + char_u **newptr, int last); // Function pointers for in-place encoding and decoding, used for // crypt_*_inplace(). "from" and "to" arguments will be equal. @@ -68,9 +72,9 @@ typedef struct { // padding to files). // This method is used for swap and undo files which have a rigid format. void (*encode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, - char_u *p2); + char_u *p2, int last); void (*decode_inplace_fn)(cryptstate_T *state, char_u *p1, size_t len, - char_u *p2); + char_u *p2, int last); } cryptmethod_T; // index is method_nr of cryptstate_T, CRYPT_M_* @@ -126,10 +130,41 @@ static cryptmethod_T cryptmethods[CRYPT_M_COUNT] = { crypt_blowfish_encode, crypt_blowfish_decode, }, + // XChaCha20 using libsodium + { + "xchacha20", + "VimCrypt~04!", +#ifdef FEAT_SODIUM + crypto_pwhash_argon2id_SALTBYTES, // 16 +#else + 16, +#endif + 8, +#ifdef CRYPT_NOT_INPLACE + FALSE, +#endif + FALSE, + NULL, + crypt_sodium_init, + NULL, NULL, + crypt_sodium_buffer_encode, crypt_sodium_buffer_decode, + NULL, NULL, + }, + // NOTE: when adding a new method, use some random bytes for the magic key, // to avoid that a text file is recognized as encrypted. }; +#ifdef FEAT_SODIUM +typedef struct { + size_t count; + unsigned char key[crypto_box_SEEDBYTES]; + // 32, same as crypto_secretstream_xchacha20poly1305_KEYBYTES + crypto_secretstream_xchacha20poly1305_state + state; +} sodium_state_T; +#endif + #define CRYPT_MAGIC_LEN 12 // cannot change static char crypt_magic_head[] = "VimCrypt~"; @@ -215,6 +250,26 @@ crypt_get_header_len(int method_nr) + cryptmethods[method_nr].seed_len; } + +/* + * Get maximum crypt method specific length of the file header in bytes. + */ + int +crypt_get_max_header_len() +{ + int i; + int max = 0; + int temp = 0; + + for (i = 0; i < CRYPT_M_COUNT; ++i) + { + temp = crypt_get_header_len(i); + if (temp > max) + max = temp; + } + return max; +} + /* * Set the crypt method for buffer "buf" to "method_nr" using the int value as * returned by crypt_method_nr_from_name(). @@ -260,7 +315,7 @@ crypt_create( state->method_nr = method_nr; if (cryptmethods[method_nr].init_fn( - state, key, salt, salt_len, seed, seed_len) == FAIL) + state, key, salt, salt_len, seed, seed_len) == FAIL) { vim_free(state); return NULL; @@ -365,9 +420,18 @@ crypt_create_for_writing( // TODO: Should this be crypt method specific? (Probably not worth // it). sha2_seed is pretty bad for large amounts of entropy, so make // that into something which is suitable for anything. - sha2_seed(salt, salt_len, seed, seed_len); +#ifdef FEAT_SODIUM + if (sodium_init() >= 0) + { + if (salt_len > 0) + randombytes_buf(salt, salt_len); + if (seed_len > 0) + randombytes_buf(seed, seed_len); + } + else +#endif + sha2_seed(salt, salt_len, seed, seed_len); } - state = crypt_create(method_nr, key, salt, salt_len, seed, seed_len); if (state == NULL) VIM_CLEAR(*header); @@ -380,7 +444,15 @@ crypt_create_for_writing( void crypt_free_state(cryptstate_T *state) { - vim_free(state->method_state); +#ifdef FEAT_SODIUM + if (state->method_nr == CRYPT_M_SOD) + { + sodium_memzero(state->method_state, sizeof(sodium_state_T)); + sodium_free(state->method_state); + } + else +#endif + vim_free(state->method_state); vim_free(state); } @@ -395,21 +467,22 @@ crypt_encode_alloc( cryptstate_T *state, char_u *from, size_t len, - char_u **newptr) + char_u **newptr, + int last) { cryptmethod_T *method = &cryptmethods[state->method_nr]; if (method->encode_buffer_fn != NULL) // Has buffer function, pass through. - return method->encode_buffer_fn(state, from, len, newptr); + return method->encode_buffer_fn(state, from, len, newptr, last); if (len == 0) // Not buffering, just return EOF. return (long)len; - *newptr = alloc(len); + *newptr = alloc(len + 50); if (*newptr == NULL) return -1; - method->encode_fn(state, from, len, *newptr); + method->encode_fn(state, from, len, *newptr, last); return (long)len; } @@ -423,13 +496,14 @@ crypt_decode_alloc( cryptstate_T *state, char_u *ptr, long len, - char_u **newptr) + char_u **newptr, + int last) { cryptmethod_T *method = &cryptmethods[state->method_nr]; if (method->decode_buffer_fn != NULL) // Has buffer function, pass through. - return method->decode_buffer_fn(state, ptr, len, newptr); + return method->decode_buffer_fn(state, ptr, len, newptr, last); if (len == 0) // Not buffering, just return EOF. @@ -438,7 +512,7 @@ crypt_decode_alloc( *newptr = alloc(len); if (*newptr == NULL) return -1; - method->decode_fn(state, ptr, len, *newptr); + method->decode_fn(state, ptr, len, *newptr, last); return len; } #endif @@ -451,9 +525,10 @@ crypt_encode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last) { - cryptmethods[state->method_nr].encode_fn(state, from, len, to); + cryptmethods[state->method_nr].encode_fn(state, from, len, to, last); } #if 0 // unused @@ -465,9 +540,10 @@ crypt_decode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last) { - cryptmethods[state->method_nr].decode_fn(state, from, len, to); + cryptmethods[state->method_nr].decode_fn(state, from, len, to, last); } #endif @@ -478,9 +554,11 @@ crypt_decode( crypt_encode_inplace( cryptstate_T *state, char_u *buf, - size_t len) + size_t len, + int last) { - cryptmethods[state->method_nr].encode_inplace_fn(state, buf, len, buf); + cryptmethods[state->method_nr].encode_inplace_fn(state, buf, len, + buf, last); } /* @@ -490,9 +568,11 @@ crypt_encode_inplace( crypt_decode_inplace( cryptstate_T *state, char_u *buf, - size_t len) + size_t len, + int last) { - cryptmethods[state->method_nr].decode_inplace_fn(state, buf, len, buf); + cryptmethods[state->method_nr].decode_inplace_fn(state, buf, len, + buf, last); } /* @@ -525,6 +605,26 @@ crypt_check_method(int method) } } +#ifdef FEAT_SODIUM + static void +crypt_check_swapfile_curbuf(void) +{ + int method = crypt_get_method_nr(curbuf); + if (method == CRYPT_M_SOD) + { + // encryption uses padding and MAC, that does not work very well with + // swap and undo files, so disable them + mf_close_file(curbuf, TRUE); // remove the swap file + set_option_value((char_u *)"swf", 0, NULL, OPT_LOCAL); +#ifdef FEAT_PERSISTENT_UNDO + set_option_value((char_u *)"udf", 0, NULL, OPT_LOCAL); +#endif + msg_scroll = TRUE; + msg(_("Note: Encryption of swapfile not supported, disabling swap- and undofile")); + } +} +#endif + void crypt_check_current_method(void) { @@ -576,6 +676,9 @@ crypt_get_key( set_option_value((char_u *)"key", 0L, p1, OPT_LOCAL); crypt_free_key(p1); p1 = curbuf->b_p_key; +#ifdef FEAT_SODIUM + crypt_check_swapfile_curbuf(); +#endif } break; } @@ -583,10 +686,13 @@ crypt_get_key( } // since the user typed this, no need to wait for return - if (msg_didout) - msg_putchar('\n'); - need_wait_return = FALSE; - msg_didout = FALSE; + if (crypt_get_method_nr(curbuf) != CRYPT_M_SOD) + { + if (msg_didout) + msg_putchar('\n'); + need_wait_return = FALSE; + msg_didout = FALSE; + } crypt_free_key(p2); return p1; @@ -610,4 +716,270 @@ crypt_append_msg( } } + int +crypt_sodium_init( + cryptstate_T *state UNUSED, + char_u *key UNUSED, + char_u *salt UNUSED, + int salt_len UNUSED, + char_u *seed UNUSED, + int seed_len UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + unsigned char dkey[crypto_box_SEEDBYTES]; // 32 + sodium_state_T *sd_state; + + if (sodium_init() < 0) + return FAIL; + + sd_state = (sodium_state_T *)sodium_malloc(sizeof(sodium_state_T)); + sodium_memzero(sd_state, sizeof(sodium_state_T)); + + // derive a key from the password + if (crypto_pwhash(dkey, sizeof(dkey), (const char *)key, STRLEN(key), salt, + crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, + crypto_pwhash_ALG_DEFAULT) != 0) + { + // out of memory + sodium_free(sd_state); + return FAIL; + } + memcpy(sd_state->key, dkey, crypto_box_SEEDBYTES); + sd_state->count = 0; + state->method_state = sd_state; + + return OK; +# else + emsg(e_libsodium_not_built_in); + return FAIL; +# endif +} + +/* + * Encrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + * Call needs to ensure that there is enough space in to (for the header) + */ +#if 0 // Currently unused + void +crypt_sodium_encode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u *to UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + sodium_state_T *sod_st = state->method_state; + unsigned char tag = last + ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; + + if (sod_st->count == 0) + { + if (len <= crypto_secretstream_xchacha20poly1305_HEADERBYTES) + { + emsg(e_libsodium_cannot_encrypt_header); + return; + } + crypto_secretstream_xchacha20poly1305_init_push(&sod_st->state, + to, sod_st->key); + to += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + } + + if (sod_st->count && len <= crypto_secretstream_xchacha20poly1305_ABYTES) + { + emsg(e_libsodium_cannot_encrypt_buffer); + return; + } + + crypto_secretstream_xchacha20poly1305_push(&sod_st->state, to, NULL, + from, len, NULL, 0, tag); + + sod_st->count++; +# endif +} +#endif + +/* + * Decrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ +#if 0 // Currently unused + void +crypt_sodium_decode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u *to UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + sodium_state_T *sod_st = state->method_state; + unsigned char tag; + unsigned long long buf_len; + char_u *p1 = from; + char_u *p2 = to; + char_u *buf_out; + + if (sod_st->count == 0 + && len <= crypto_secretstream_xchacha20poly1305_HEADERBYTES) + { + emsg(e_libsodium_cannot_decrypt_header); + return; + } + + buf_out = (char_u *)alloc(len); + + if (buf_out == NULL) + { + emsg(e_libsodium_cannot_allocate_buffer); + return; + } + if (sod_st->count == 0) + { + if (crypto_secretstream_xchacha20poly1305_init_pull( + &sod_st->state, from, sod_st->key) != 0) + { + emsg(e_libsodium_decryption_failed_header_incomplete); + goto fail; + } + + from += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + len -= crypto_secretstream_xchacha20poly1305_HEADERBYTES; + + if (p1 == p2) + to += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + } + + if (sod_st->count && len <= crypto_secretstream_xchacha20poly1305_ABYTES) + { + emsg(e_libsodium_cannot_decrypt_buffer); + goto fail; + } + if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state, + buf_out, &buf_len, &tag, from, len, NULL, 0) != 0) + { + emsg(e_libsodium_decryption_failed); + goto fail; + } + sod_st->count++; + + if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last) + { + emsg(e_libsodium_decryption_failed_premature); + goto fail; + } + if (p1 == p2) + mch_memmove(p2, buf_out, buf_len); + +fail: + vim_free(buf_out); +# endif +} +#endif + +/* + * Encrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + long +crypt_sodium_buffer_encode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u **buf_out UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + unsigned long long out_len; + char_u *ptr; + unsigned char tag = last + ? crypto_secretstream_xchacha20poly1305_TAG_FINAL : 0; + int length; + sodium_state_T *sod_st = state->method_state; + int first = (sod_st->count == 0); + + length = (int)len + crypto_secretstream_xchacha20poly1305_ABYTES + + (first ? crypto_secretstream_xchacha20poly1305_HEADERBYTES : 0); + *buf_out = alloc_clear(length); + if (*buf_out == NULL) + { + emsg(e_libsodium_cannot_allocate_buffer); + return -1; + } + ptr = *buf_out; + + if (first) + { + crypto_secretstream_xchacha20poly1305_init_push(&sod_st->state, + ptr, sod_st->key); + ptr += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + } + + crypto_secretstream_xchacha20poly1305_push(&sod_st->state, ptr, + &out_len, from, len, NULL, 0, tag); + + sod_st->count++; + return out_len + (first + ? crypto_secretstream_xchacha20poly1305_HEADERBYTES : 0); +# else + return -1; +# endif +} + +/* + * Decrypt "from[len]" into "to[len]". + * "from" and "to" can be equal to encrypt in place. + */ + long +crypt_sodium_buffer_decode( + cryptstate_T *state UNUSED, + char_u *from UNUSED, + size_t len UNUSED, + char_u **buf_out UNUSED, + int last UNUSED) +{ +# ifdef FEAT_SODIUM + // crypto_box_SEEDBYTES == crypto_secretstream_xchacha20poly1305_KEYBYTES + sodium_state_T *sod_st = state->method_state; + unsigned char tag; + unsigned long long out_len; + *buf_out = alloc_clear(len); + if (*buf_out == NULL) + { + emsg(e_libsodium_cannot_allocate_buffer); + return -1; + } + + if (sod_st->count == 0) + { + if (crypto_secretstream_xchacha20poly1305_init_pull(&sod_st->state, + from, sod_st->key) != 0) + { + emsg(e_libsodium_decryption_failed_header_incomplete); + return -1; + } + from += crypto_secretstream_xchacha20poly1305_HEADERBYTES; + len -= crypto_secretstream_xchacha20poly1305_HEADERBYTES; + sod_st->count++; + } + if (crypto_secretstream_xchacha20poly1305_pull(&sod_st->state, + *buf_out, &out_len, &tag, from, len, NULL, 0) != 0) + { + emsg(e_libsodium_decryption_failed); + return -1; + } + + if (tag == crypto_secretstream_xchacha20poly1305_TAG_FINAL && !last) + emsg(e_libsodium_decryption_failed_premature); + return (long) out_len; +# else + return -1; +# endif +} + #endif // FEAT_CRYPT diff --git a/src/crypt_zip.c b/src/crypt_zip.c index 42abe9350b..b11d7a329f 100644 --- a/src/crypt_zip.c +++ b/src/crypt_zip.c @@ -114,7 +114,8 @@ crypt_zip_encode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last UNUSED) { zip_state_T *zs = state->method_state; size_t i; @@ -137,7 +138,8 @@ crypt_zip_decode( cryptstate_T *state, char_u *from, size_t len, - char_u *to) + char_u *to, + int last UNUSED) { zip_state_T *zs = state->method_state; size_t i; diff --git a/src/debugger.c b/src/debugger.c index b96846310a..52a04907ef 100644 --- a/src/debugger.c +++ b/src/debugger.c @@ -606,7 +606,7 @@ dbg_parsearg( } if (bp->dbg_type == DBG_FUNC) - bp->dbg_name = vim_strsave(p); + bp->dbg_name = vim_strsave(STRNCMP(p, "g:", 2) == 0 ? p + 2 : p); else if (here) bp->dbg_name = vim_strsave(curbuf->b_ffname); else if (bp->dbg_type == DBG_EXPR) diff --git a/src/drawline.c b/src/drawline.c index 58922d557f..ba5ad99115 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1121,8 +1121,9 @@ win_line( int t; // like rl_mirror(), but keep the space at the end - p2 = skiptowhite(extra) - 1; - for (p1 = extra; p1 < p2; ++p1, --p2) + p2 = skipwhite(extra); + p2 = skiptowhite(p2) - 1; + for (p1 = skipwhite(extra); p1 < p2; ++p1, --p2) { t = *p1; *p1 = *p2; diff --git a/src/errors.h b/src/errors.h index 63e2659f73..8a2461b31e 100644 --- a/src/errors.h +++ b/src/errors.h @@ -427,3 +427,22 @@ EXTERN char e_call_to_function_that_failed_to_compile_str[] INIT(= N_("E1191: Call to function that failed to compile: %s")); EXTERN char e_empty_function_name[] INIT(= N_("E1192: Empty function name")); +// libsodium +EXTERN char e_libsodium_not_built_in[] + INIT(= N_("E1193: cryptmethod xchacha20 not built into this Vim")); +EXTERN char e_libsodium_cannot_encrypt_header[] + INIT(= N_("E1194: Cannot encrypt header, not enough space")); +EXTERN char e_libsodium_cannot_encrypt_buffer[] + INIT(= N_("E1195: Cannot encrypt buffer, not enough space")); +EXTERN char e_libsodium_cannot_decrypt_header[] + INIT(= N_("E1196: Cannot decrypt header, not enough space")); +EXTERN char e_libsodium_cannot_allocate_buffer[] + INIT(= N_("E1197: Cannot allocate_buffer for encryption")); +EXTERN char e_libsodium_decryption_failed_header_incomplete[] + INIT(= N_("E1198: Decryption failed: Header incomplete!")); +EXTERN char e_libsodium_cannot_decrypt_buffer[] + INIT(= N_("E1199: Cannot decrypt buffer, not enough space")); +EXTERN char e_libsodium_decryption_failed[] + INIT(= N_("E1200: Decryption failed!")); +EXTERN char e_libsodium_decryption_failed_premature[] + INIT(= N_("E1201: Decryption failed: pre-mature end of file!")); diff --git a/src/eval.c b/src/eval.c index ffc7122cde..33ea8504a2 100644 --- a/src/eval.c +++ b/src/eval.c @@ -2218,12 +2218,15 @@ eval0( int did_emsg_before = did_emsg; int called_emsg_before = called_emsg; int flags = evalarg == NULL ? 0 : evalarg->eval_flags; + int end_error = FALSE; p = skipwhite(arg); ret = eval1(&p, rettv, evalarg); p = skipwhite(p); - if (ret == FAIL || !ends_excmd2(arg, p)) + if (ret != FAIL) + end_error = !ends_excmd2(arg, p); + if (ret == FAIL || end_error) { if (ret != FAIL) clear_tv(rettv); @@ -2238,7 +2241,12 @@ eval0( && called_emsg == called_emsg_before && (flags & EVAL_CONSTANT) == 0 && (!in_vim9script() || !vim9_bad_comment(p))) - semsg(_(e_invexpr2), arg); + { + if (end_error) + semsg(_(e_trailing_arg), p); + else + semsg(_(e_invexpr2), arg); + } // Some of the expression may not have been consumed. Do not check for // a next command to avoid more errors, unless "|" is following, which diff --git a/src/evalfunc.c b/src/evalfunc.c index 158e4347fe..d6834447df 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -301,6 +301,27 @@ arg_list_or_blob(type_T *type, argcontext_T *context) return FAIL; } +/* + * Check "type" is a string or a list of strings. + */ + static int +arg_string_or_list(type_T *type, argcontext_T *context) +{ + if (type->tt_type == VAR_ANY || type->tt_type == VAR_STRING) + return OK; + if (type->tt_type != VAR_LIST) + { + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; + } + if (type->tt_member->tt_type == VAR_ANY + || type->tt_member->tt_type == VAR_STRING) + return OK; + + arg_type_mismatch(&t_list_string, type, context->arg_idx + 1); + return FAIL; +} + /* * Check "type" is a list or a dict. */ @@ -382,11 +403,16 @@ arg_extend3(type_T *type, argcontext_T *context) * Lists of functions that check the argument types of a builtin function. */ argcheck_T arg1_string[] = {arg_string}; -argcheck_T arg3_string_nr_bool[] = {arg_string, arg_number, arg_bool}; +argcheck_T arg1_number[] = {arg_number}; argcheck_T arg1_float_or_nr[] = {arg_float_or_nr}; +argcheck_T arg2_float_or_nr[] = {arg_float_or_nr, arg_float_or_nr}; +argcheck_T arg2_number[] = {arg_number, arg_number}; argcheck_T arg2_listblob_item[] = {arg_list_or_blob, arg_item_of_prev}; +argcheck_T arg2_execute[] = {arg_string_or_list, arg_string}; argcheck_T arg23_extend[] = {arg_list_or_dict, arg_same_as_prev, arg_extend3}; argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, arg_extend3}; +argcheck_T arg3_string[] = {arg_string, arg_string, arg_string}; +argcheck_T arg3_string_nr_bool[] = {arg_string, arg_number, arg_bool}; argcheck_T arg3_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number}; /* @@ -648,25 +674,25 @@ static funcentry_T global_functions[] = { {"abs", 1, 1, FEARG_1, arg1_float_or_nr, ret_any, FLOAT_FUNC(f_abs)}, - {"acos", 1, 1, FEARG_1, NULL, + {"acos", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_acos)}, {"add", 2, 2, FEARG_1, NULL /* arg2_listblob_item */, ret_first_arg, f_add}, - {"and", 2, 2, FEARG_1, NULL, + {"and", 2, 2, FEARG_1, arg2_number, ret_number, f_and}, {"append", 2, 2, FEARG_2, NULL, ret_number_bool, f_append}, {"appendbufline", 3, 3, FEARG_3, NULL, ret_number_bool, f_appendbufline}, - {"argc", 0, 1, 0, NULL, + {"argc", 0, 1, 0, arg1_number, ret_number, f_argc}, {"argidx", 0, 0, 0, NULL, ret_number, f_argidx}, - {"arglistid", 0, 2, 0, NULL, + {"arglistid", 0, 2, 0, arg2_number, ret_number, f_arglistid}, - {"argv", 0, 2, 0, NULL, + {"argv", 0, 2, 0, arg2_number, ret_argv, f_argv}, - {"asin", 1, 1, FEARG_1, NULL, + {"asin", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_asin)}, {"assert_beeps", 1, 2, FEARG_1, NULL, ret_number_bool, f_assert_beeps}, @@ -694,9 +720,9 @@ static funcentry_T global_functions[] = ret_number_bool, f_assert_report}, {"assert_true", 1, 2, FEARG_1, NULL, ret_number_bool, f_assert_true}, - {"atan", 1, 1, FEARG_1, NULL, + {"atan", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_atan)}, - {"atan2", 2, 2, FEARG_1, NULL, + {"atan2", 2, 2, FEARG_1, arg2_float_or_nr, ret_float, FLOAT_FUNC(f_atan2)}, {"balloon_gettext", 0, 0, 0, NULL, ret_string, @@ -758,7 +784,7 @@ static funcentry_T global_functions[] = ret_number, f_byteidxcomp}, {"call", 2, 3, FEARG_1, NULL, ret_any, f_call}, - {"ceil", 1, 1, FEARG_1, NULL, + {"ceil", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_ceil)}, {"ch_canread", 1, 1, FEARG_1, NULL, ret_number_bool, JOB_FUNC(f_ch_canread)}, @@ -810,7 +836,7 @@ static funcentry_T global_functions[] = ret_string, f_chdir}, {"cindent", 1, 1, FEARG_1, NULL, ret_number, f_cindent}, - {"clearmatches", 0, 1, FEARG_1, NULL, + {"clearmatches", 0, 1, FEARG_1, arg1_number, ret_void, f_clearmatches}, {"col", 1, 1, FEARG_1, NULL, ret_number, f_col}, @@ -826,9 +852,9 @@ static funcentry_T global_functions[] = ret_number, f_confirm}, {"copy", 1, 1, FEARG_1, NULL, ret_first_arg, f_copy}, - {"cos", 1, 1, FEARG_1, NULL, + {"cos", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_cos)}, - {"cosh", 1, 1, FEARG_1, NULL, + {"cosh", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_cosh)}, {"count", 2, 4, FEARG_1, NULL, ret_number, f_count}, @@ -836,7 +862,7 @@ static funcentry_T global_functions[] = ret_number, f_cscope_connection}, {"cursor", 1, 3, FEARG_1, NULL, ret_number, f_cursor}, - {"debugbreak", 1, 1, FEARG_1, NULL, + {"debugbreak", 1, 1, FEARG_1, arg1_number, ret_number, #ifdef MSWIN f_debugbreak @@ -870,13 +896,13 @@ static funcentry_T global_functions[] = ret_number_bool, f_eventhandler}, {"executable", 1, 1, FEARG_1, NULL, ret_number, f_executable}, - {"execute", 1, 2, FEARG_1, NULL, + {"execute", 1, 2, FEARG_1, arg2_execute, ret_string, f_execute}, {"exepath", 1, 1, FEARG_1, NULL, ret_string, f_exepath}, {"exists", 1, 1, FEARG_1, NULL, ret_number_bool, f_exists}, - {"exp", 1, 1, FEARG_1, NULL, + {"exp", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_exp)}, {"expand", 1, 3, FEARG_1, NULL, ret_any, f_expand}, @@ -904,11 +930,11 @@ static funcentry_T global_functions[] = ret_list_any, f_flatten}, {"flattennew", 1, 2, FEARG_1, NULL, ret_list_any, f_flattennew}, - {"float2nr", 1, 1, FEARG_1, NULL, + {"float2nr", 1, 1, FEARG_1, arg1_float_or_nr, ret_number, FLOAT_FUNC(f_float2nr)}, - {"floor", 1, 1, FEARG_1, NULL, + {"floor", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_floor)}, - {"fmod", 2, 2, FEARG_1, NULL, + {"fmod", 2, 2, FEARG_1, arg2_float_or_nr, ret_float, FLOAT_FUNC(f_fmod)}, {"fnameescape", 1, 1, FEARG_1, NULL, ret_string, f_fnameescape}, @@ -964,11 +990,11 @@ static funcentry_T global_functions[] = ret_string, f_getcmdwintype}, {"getcompletion", 2, 3, FEARG_1, NULL, ret_list_string, f_getcompletion}, - {"getcurpos", 0, 1, FEARG_1, NULL, + {"getcurpos", 0, 1, FEARG_1, arg1_number, ret_list_number, f_getcurpos}, - {"getcursorcharpos", 0, 1, FEARG_1, NULL, + {"getcursorcharpos", 0, 1, FEARG_1, arg1_number, ret_list_number, f_getcursorcharpos}, - {"getcwd", 0, 2, FEARG_1, NULL, + {"getcwd", 0, 2, FEARG_1, arg2_number, ret_string, f_getcwd}, {"getenv", 1, 1, FEARG_1, NULL, ret_any, f_getenv}, @@ -984,7 +1010,7 @@ static funcentry_T global_functions[] = ret_string, f_getftype}, {"getimstatus", 0, 0, 0, NULL, ret_number_bool, f_getimstatus}, - {"getjumplist", 0, 2, FEARG_1, NULL, + {"getjumplist", 0, 2, FEARG_1, arg2_number, ret_list_any, f_getjumplist}, {"getline", 1, 2, FEARG_1, NULL, ret_f_getline, f_getline}, @@ -992,7 +1018,7 @@ static funcentry_T global_functions[] = ret_list_or_dict_1, f_getloclist}, {"getmarklist", 0, 1, FEARG_1, NULL, ret_list_dict_any, f_getmarklist}, - {"getmatches", 0, 1, 0, NULL, + {"getmatches", 0, 1, 0, arg1_number, ret_list_dict_any, f_getmatches}, {"getmousepos", 0, 0, 0, NULL, ret_dict_number, f_getmousepos}, @@ -1008,19 +1034,19 @@ static funcentry_T global_functions[] = ret_dict_any, f_getreginfo}, {"getregtype", 0, 1, FEARG_1, NULL, ret_string, f_getregtype}, - {"gettabinfo", 0, 1, FEARG_1, NULL, + {"gettabinfo", 0, 1, FEARG_1, arg1_number, ret_list_dict_any, f_gettabinfo}, {"gettabvar", 2, 3, FEARG_1, NULL, ret_any, f_gettabvar}, {"gettabwinvar", 3, 4, FEARG_1, NULL, ret_any, f_gettabwinvar}, - {"gettagstack", 0, 1, FEARG_1, NULL, + {"gettagstack", 0, 1, FEARG_1, arg1_number, ret_dict_any, f_gettagstack}, {"gettext", 1, 1, FEARG_1, NULL, ret_string, f_gettext}, - {"getwininfo", 0, 1, FEARG_1, NULL, + {"getwininfo", 0, 1, FEARG_1, arg1_number, ret_list_dict_any, f_getwininfo}, - {"getwinpos", 0, 1, FEARG_1, NULL, + {"getwinpos", 0, 1, FEARG_1, arg1_number, ret_list_number, f_getwinpos}, {"getwinposx", 0, 0, 0, NULL, ret_number, f_getwinposx}, @@ -1038,7 +1064,7 @@ static funcentry_T global_functions[] = ret_number_bool, f_has}, {"has_key", 2, 2, FEARG_1, NULL, ret_number_bool, f_has_key}, - {"haslocaldir", 0, 2, FEARG_1, NULL, + {"haslocaldir", 0, 2, FEARG_1, arg2_number, ret_number, f_haslocaldir}, {"hasmapto", 1, 3, FEARG_1, NULL, ret_number_bool, f_hasmapto}, @@ -1082,15 +1108,15 @@ static funcentry_T global_functions[] = ret_first_arg, f_insert}, {"interrupt", 0, 0, 0, NULL, ret_void, f_interrupt}, - {"invert", 1, 1, FEARG_1, NULL, + {"invert", 1, 1, FEARG_1, arg1_number, ret_number, f_invert}, {"isdirectory", 1, 1, FEARG_1, NULL, ret_number_bool, f_isdirectory}, - {"isinf", 1, 1, FEARG_1, NULL, + {"isinf", 1, 1, FEARG_1, arg1_float_or_nr, ret_number, MATH_FUNC(f_isinf)}, {"islocked", 1, 1, FEARG_1, NULL, ret_number_bool, f_islocked}, - {"isnan", 1, 1, FEARG_1, NULL, + {"isnan", 1, 1, FEARG_1, arg1_float_or_nr, ret_number_bool, MATH_FUNC(f_isnan)}, {"items", 1, 1, FEARG_1, NULL, ret_list_any, f_items}, @@ -1138,13 +1164,13 @@ static funcentry_T global_functions[] = ret_number, f_listener_add}, {"listener_flush", 0, 1, FEARG_1, NULL, ret_void, f_listener_flush}, - {"listener_remove", 1, 1, FEARG_1, NULL, + {"listener_remove", 1, 1, FEARG_1, arg1_number, ret_number_bool, f_listener_remove}, {"localtime", 0, 0, 0, NULL, ret_number, f_localtime}, - {"log", 1, 1, FEARG_1, NULL, + {"log", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_log)}, - {"log10", 1, 1, FEARG_1, NULL, + {"log10", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_log10)}, {"luaeval", 1, 2, FEARG_1, NULL, ret_any, @@ -1170,9 +1196,9 @@ static funcentry_T global_functions[] = ret_number, f_matchadd}, {"matchaddpos", 2, 5, FEARG_1, NULL, ret_number, f_matchaddpos}, - {"matcharg", 1, 1, FEARG_1, NULL, + {"matcharg", 1, 1, FEARG_1, arg1_number, ret_list_string, f_matcharg}, - {"matchdelete", 1, 2, FEARG_1, NULL, + {"matchdelete", 1, 2, FEARG_1, arg2_number, ret_number_bool, f_matchdelete}, {"matchend", 2, 4, FEARG_1, NULL, ret_number, f_matchend}, @@ -1214,7 +1240,7 @@ static funcentry_T global_functions[] = ret_number, f_nextnonblank}, {"nr2char", 1, 2, FEARG_1, NULL, ret_string, f_nr2char}, - {"or", 2, 2, FEARG_1, NULL, + {"or", 2, 2, FEARG_1, arg2_number, ret_number, f_or}, {"pathshorten", 1, 2, FEARG_1, NULL, ret_string, f_pathshorten}, @@ -1268,7 +1294,7 @@ static funcentry_T global_functions[] = ret_void, PROP_FUNC(f_popup_settext)}, {"popup_show", 1, 1, FEARG_1, NULL, ret_void, PROP_FUNC(f_popup_show)}, - {"pow", 2, 2, FEARG_1, NULL, + {"pow", 2, 2, FEARG_1, arg2_float_or_nr, ret_float, FLOAT_FUNC(f_pow)}, {"prevnonblank", 1, 1, FEARG_1, NULL, ret_number, f_prevnonblank}, @@ -1376,7 +1402,7 @@ static funcentry_T global_functions[] = ret_string, f_resolve}, {"reverse", 1, 1, FEARG_1, NULL, ret_first_arg, f_reverse}, - {"round", 1, 1, FEARG_1, NULL, + {"round", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_round)}, {"rubyeval", 1, 1, FEARG_1, NULL, ret_any, @@ -1386,11 +1412,11 @@ static funcentry_T global_functions[] = NULL #endif }, - {"screenattr", 2, 2, FEARG_1, NULL, + {"screenattr", 2, 2, FEARG_1, arg2_number, ret_number, f_screenattr}, - {"screenchar", 2, 2, FEARG_1, NULL, + {"screenchar", 2, 2, FEARG_1, arg2_number, ret_number, f_screenchar}, - {"screenchars", 2, 2, FEARG_1, NULL, + {"screenchars", 2, 2, FEARG_1, arg2_number, ret_list_number, f_screenchars}, {"screencol", 0, 0, 0, NULL, ret_number, f_screencol}, @@ -1398,7 +1424,7 @@ static funcentry_T global_functions[] = ret_dict_number, f_screenpos}, {"screenrow", 0, 0, 0, NULL, ret_number, f_screenrow}, - {"screenstring", 2, 2, FEARG_1, NULL, + {"screenstring", 2, 2, FEARG_1, arg2_number, ret_string, f_screenstring}, {"search", 1, 5, FEARG_1, NULL, ret_number, f_search}, @@ -1426,7 +1452,7 @@ static funcentry_T global_functions[] = ret_number_bool, f_setcharpos}, {"setcharsearch", 1, 1, FEARG_1, NULL, ret_void, f_setcharsearch}, - {"setcmdpos", 1, 1, FEARG_1, NULL, + {"setcmdpos", 1, 1, FEARG_1, arg1_number, ret_number_bool, f_setcmdpos}, {"setcursorcharpos", 1, 3, FEARG_1, NULL, ret_number_bool, f_setcursorcharpos}, @@ -1464,7 +1490,7 @@ static funcentry_T global_functions[] = }, {"shellescape", 1, 2, FEARG_1, NULL, ret_string, f_shellescape}, - {"shiftwidth", 0, 1, FEARG_1, NULL, + {"shiftwidth", 0, 1, FEARG_1, arg1_number, ret_number, f_shiftwidth}, {"sign_define", 1, 2, FEARG_1, NULL, ret_any, SIGN_FUNC(f_sign_define)}, @@ -1486,9 +1512,9 @@ static funcentry_T global_functions[] = ret_list_number, SIGN_FUNC(f_sign_unplacelist)}, {"simplify", 1, 1, FEARG_1, NULL, ret_string, f_simplify}, - {"sin", 1, 1, FEARG_1, NULL, + {"sin", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_sin)}, - {"sinh", 1, 1, FEARG_1, NULL, + {"sinh", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_sinh)}, {"slice", 2, 3, FEARG_1, NULL, ret_first_arg, f_slice}, @@ -1510,7 +1536,7 @@ static funcentry_T global_functions[] = ret_list_string, f_spellsuggest}, {"split", 1, 3, FEARG_1, NULL, ret_list_string, f_split}, - {"sqrt", 1, 1, FEARG_1, NULL, + {"sqrt", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_sqrt)}, {"srand", 0, 1, FEARG_1, NULL, ret_list_number, f_srand}, @@ -1594,9 +1620,9 @@ static funcentry_T global_functions[] = ret_list_string, f_tagfiles}, {"taglist", 1, 2, FEARG_1, NULL, ret_list_dict_any, f_taglist}, - {"tan", 1, 1, FEARG_1, NULL, + {"tan", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_tan)}, - {"tanh", 1, 1, FEARG_1, NULL, + {"tanh", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_tanh)}, {"tempname", 0, 0, 0, NULL, ret_string, f_tempname}, @@ -1674,6 +1700,8 @@ static funcentry_T global_functions[] = ret_void, f_test_garbagecollect_soon}, {"test_getvalue", 1, 1, FEARG_1, NULL, ret_number, f_test_getvalue}, + {"test_gui_mouse_event", 5, 5, 0, NULL, + ret_void, f_test_gui_mouse_event}, {"test_ignore_error", 1, 1, FEARG_1, NULL, ret_void, f_test_ignore_error}, {"test_null_blob", 0, 0, 0, NULL, @@ -1726,15 +1754,15 @@ static funcentry_T global_functions[] = ret_void, TIMER_FUNC(f_timer_stop)}, {"timer_stopall", 0, 0, 0, NULL, ret_void, TIMER_FUNC(f_timer_stopall)}, - {"tolower", 1, 1, FEARG_1, NULL, + {"tolower", 1, 1, FEARG_1, arg1_string, ret_string, f_tolower}, - {"toupper", 1, 1, FEARG_1, NULL, + {"toupper", 1, 1, FEARG_1, arg1_string, ret_string, f_toupper}, - {"tr", 3, 3, FEARG_1, NULL, + {"tr", 3, 3, FEARG_1, arg3_string, ret_string, f_tr}, {"trim", 1, 3, FEARG_1, NULL, ret_string, f_trim}, - {"trunc", 1, 1, FEARG_1, NULL, + {"trunc", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_trunc)}, {"type", 1, 1, FEARG_1, NULL, ret_number, f_type}, @@ -1760,27 +1788,27 @@ static funcentry_T global_functions[] = ret_list_number, f_win_findbuf}, {"win_getid", 0, 2, FEARG_1, NULL, ret_number, f_win_getid}, - {"win_gettype", 0, 1, FEARG_1, NULL, + {"win_gettype", 0, 1, FEARG_1, arg1_number, ret_string, f_win_gettype}, - {"win_gotoid", 1, 1, FEARG_1, NULL, + {"win_gotoid", 1, 1, FEARG_1, arg1_number, ret_number_bool, f_win_gotoid}, - {"win_id2tabwin", 1, 1, FEARG_1, NULL, + {"win_id2tabwin", 1, 1, FEARG_1, arg1_number, ret_list_number, f_win_id2tabwin}, - {"win_id2win", 1, 1, FEARG_1, NULL, + {"win_id2win", 1, 1, FEARG_1, arg1_number, ret_number, f_win_id2win}, - {"win_screenpos", 1, 1, FEARG_1, NULL, + {"win_screenpos", 1, 1, FEARG_1, arg1_number, ret_list_number, f_win_screenpos}, {"win_splitmove", 2, 3, FEARG_1, NULL, ret_number_bool, f_win_splitmove}, - {"winbufnr", 1, 1, FEARG_1, NULL, + {"winbufnr", 1, 1, FEARG_1, arg1_number, ret_number, f_winbufnr}, {"wincol", 0, 0, 0, NULL, ret_number, f_wincol}, {"windowsversion", 0, 0, 0, NULL, ret_string, f_windowsversion}, - {"winheight", 1, 1, FEARG_1, NULL, + {"winheight", 1, 1, FEARG_1, arg1_number, ret_number, f_winheight}, - {"winlayout", 0, 1, FEARG_1, NULL, + {"winlayout", 0, 1, FEARG_1, arg1_number, ret_list_any, f_winlayout}, {"winline", 0, 0, 0, NULL, ret_number, f_winline}, @@ -1792,13 +1820,13 @@ static funcentry_T global_functions[] = ret_void, f_winrestview}, {"winsaveview", 0, 0, 0, NULL, ret_dict_number, f_winsaveview}, - {"winwidth", 1, 1, FEARG_1, NULL, + {"winwidth", 1, 1, FEARG_1, arg1_number, ret_number, f_winwidth}, {"wordcount", 0, 0, 0, NULL, ret_dict_number, f_wordcount}, {"writefile", 2, 3, FEARG_1, NULL, ret_number_bool, f_writefile}, - {"xor", 2, 2, FEARG_1, NULL, + {"xor", 2, 2, FEARG_1, arg2_number, ret_number, f_xor}, }; @@ -5091,6 +5119,13 @@ f_has(typval_T *argvars, typval_T *rettv) 1 #else 0 +#endif + }, + {"sodium", +#ifdef FEAT_SODIUM + 1 +#else + 0 #endif }, {"sound", diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 614d41d69f..d8c5a12c72 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -3643,6 +3643,17 @@ skip_substitute(char_u *start, int delimiter) return p; } + static int +check_regexp_delim(int c) +{ + if (isalpha(c)) + { + emsg(_("E146: Regular expressions can't be delimited by letters")); + return FAIL; + } + return OK; +} + /* * Perform a substitution from line eap->line1 to line eap->line2 using the * command pointed to by eap->arg which should be of the form: @@ -3705,11 +3716,9 @@ ex_substitute(exarg_T *eap) && vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL) { // don't accept alphanumeric for separator - if (isalpha(*cmd)) - { - emsg(_("E146: Regular expressions can't be delimited by letters")); + if (check_regexp_delim(*cmd) == FAIL) return; - } + /* * undocumented vi feature: * "\/sub/" and "\?sub?" use last used search pattern (almost like @@ -4909,6 +4918,10 @@ ex_global(exarg_T *eap) emsg(_("E148: Regular expression missing from global")); return; } + else if (check_regexp_delim(*cmd) == FAIL) + { + return; + } else { delim = *cmd; // get the delimiter diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 66afe1b647..1db2402c27 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3491,6 +3491,8 @@ find_ex_command( // can't be an assignment. if (*eap->cmd == '[') { + char_u *eq; + p = to_name_const_end(eap->cmd); if (p == eap->cmd && *p == '[') { @@ -3499,12 +3501,19 @@ find_ex_command( p = skip_var_list(eap->cmd, TRUE, &count, &semicolon, TRUE); } - if (p == NULL || p == eap->cmd || *skipwhite(p) != '=') + eq = p; + if (eq != NULL) + { + eq = skipwhite(eq); + if (vim_strchr((char_u *)"+-*/%", *eq) != NULL) + ++eq; + } + if (p == NULL || p == eap->cmd || *eq != '=') { eap->cmdidx = CMD_eval; return eap->cmd; } - if (p > eap->cmd && *skipwhite(p) == '=') + if (p > eap->cmd && *eq == '=') { eap->cmdidx = CMD_var; return eap->cmd; @@ -4529,9 +4538,6 @@ invalid_range(exarg_T *eap) #endif break; case ADDR_UNSIGNED: - if (eap->line2 < 0) - return _(e_invrange); - break; case ADDR_NONE: // Will give an error elsewhere. break; diff --git a/src/feature.h b/src/feature.h index f80cd777ef..ebcdbda3cf 100644 --- a/src/feature.h +++ b/src/feature.h @@ -596,6 +596,13 @@ # define FEAT_SOUND_CANBERRA #endif +/* + * libsodium - add cryptography support + */ +#if defined(HAVE_SODIUM) && defined(FEAT_BIG) +# define FEAT_SODIUM +#endif + // There are two ways to use XPM. #if (defined(HAVE_XM_XPMP_H) && defined(FEAT_GUI_MOTIF)) \ || defined(HAVE_X11_XPM_H) diff --git a/src/fileio.c b/src/fileio.c index db8d739af8..6384fb0fe0 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -13,6 +13,10 @@ #include "vim.h" +#ifdef FEAT_SODIUM +# include +#endif + #if defined(__TANDEM) # include // for SSIZE_MAX #endif @@ -148,6 +152,8 @@ readfile( char_u *p; off_T filesize = 0; int skip_read = FALSE; + off_T filesize_disk = 0; // file size read from disk + off_T filesize_count = 0; // counter #ifdef FEAT_CRYPT char_u *cryptkey = NULL; int did_ask_for_key = FALSE; @@ -215,6 +221,7 @@ readfile( int using_b_ffname; int using_b_fname; static char *msg_is_a_directory = N_("is a directory"); + int eof; au_did_filetype = FALSE; // reset before triggering any autocommands @@ -405,6 +412,7 @@ readfile( { buf_store_time(curbuf, &st, fname); curbuf->b_mtime_read = curbuf->b_mtime; + filesize_disk = st.st_size; #ifdef UNIX /* * Use the protection bits of the original file for the swap file. @@ -1080,6 +1088,7 @@ retry: { linerest = 0; filesize = 0; + filesize_count = 0; skip_count = lines_to_skip; read_count = lines_to_read; conv_restlen = 0; @@ -1204,6 +1213,7 @@ retry: * Read bytes from curbuf. Used for converting text read * from stdin. */ + eof = FALSE; if (read_buf_lnum > from) size = 0; else @@ -1252,6 +1262,7 @@ retry: if (!curbuf->b_p_eol) --tlen; size = tlen; + eof = TRUE; break; } } @@ -1263,7 +1274,23 @@ retry: /* * Read bytes from the file. */ +# ifdef FEAT_SODIUM + // Let the crypt layer work with a buffer size of 8192 + if (filesize == 0) + // set size to 8K + Sodium Crypt Metadata + size = WRITEBUFSIZE + crypt_get_max_header_len() + + crypto_secretstream_xchacha20poly1305_HEADERBYTES + + crypto_secretstream_xchacha20poly1305_ABYTES; + + else if (filesize > 0 && (curbuf->b_cryptstate != NULL && + curbuf->b_cryptstate->method_nr == CRYPT_M_SOD)) + size = WRITEBUFSIZE + crypto_secretstream_xchacha20poly1305_ABYTES; +# endif + eof = size; size = read_eintr(fd, ptr, size); + filesize_count += size; + // hit end of file + eof = (size < eof || filesize_count == filesize_disk); } #ifdef FEAT_CRYPT @@ -1285,7 +1312,8 @@ retry: if (crypt_works_inplace(curbuf->b_cryptstate)) { # endif - crypt_decode_inplace(curbuf->b_cryptstate, ptr, size); + crypt_decode_inplace(curbuf->b_cryptstate, ptr, + size, eof); # ifdef CRYPT_NOT_INPLACE } else @@ -1294,8 +1322,16 @@ retry: int decrypted_size; decrypted_size = crypt_decode_alloc( - curbuf->b_cryptstate, ptr, size, &newptr); + curbuf->b_cryptstate, ptr, size, + &newptr, eof); + if (decrypted_size < 0) + { + // error message already given + error = TRUE; + vim_free(newptr); + break; + } // If the crypt layer is buffering, not producing // anything yet, need to read more. if (decrypted_size == 0) @@ -1325,6 +1361,7 @@ retry: if (newptr != NULL) mch_memmove(new_buffer + linerest, newptr, decrypted_size); + vim_free(newptr); } if (new_buffer != NULL) @@ -1334,6 +1371,7 @@ retry: new_buffer = NULL; line_start = buffer; ptr = buffer + linerest; + real_size = size; } size = decrypted_size; } diff --git a/src/globals.h b/src/globals.h index 971d7c75d2..82755d819e 100644 --- a/src/globals.h +++ b/src/globals.h @@ -418,6 +418,9 @@ EXTERN type_T t_blob INIT6(VAR_BLOB, 0, 0, TTFLAG_STATIC, NULL, NULL); EXTERN type_T t_job INIT6(VAR_JOB, 0, 0, TTFLAG_STATIC, NULL, NULL); EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL); +// Special value used for @#. +EXTERN type_T t_number_or_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL); + EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_unknown, NULL); EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_void, NULL); EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_any, NULL); diff --git a/src/memline.c b/src/memline.c index 58582c9d69..f1c2a8a524 100644 --- a/src/memline.c +++ b/src/memline.c @@ -48,6 +48,11 @@ # include #endif +// for randombytes_buf +#ifdef FEAT_SODIUM +# include +#endif + #if defined(SASC) || defined(__amigaos4__) # include // for Open() and Close() #endif @@ -64,12 +69,14 @@ typedef struct pointer_entry PTR_EN; // block/line-count pair #define BLOCK0_ID1_C0 'c' // block 0 id 1 'cm' 0 #define BLOCK0_ID1_C1 'C' // block 0 id 1 'cm' 1 #define BLOCK0_ID1_C2 'd' // block 0 id 1 'cm' 2 +#define BLOCK0_ID1_C3 'S' // block 0 id 1 'cm' 3 - but not actually used #if defined(FEAT_CRYPT) static int id1_codes[] = { BLOCK0_ID1_C0, // CRYPT_M_ZIP BLOCK0_ID1_C1, // CRYPT_M_BF BLOCK0_ID1_C2, // CRYPT_M_BF2 + BLOCK0_ID1_C3, // CRYPT_M_SOD - Unused! }; #endif @@ -426,11 +433,15 @@ ml_set_mfp_crypt(buf_T *buf) { int method_nr = crypt_get_method_nr(buf); - if (method_nr > CRYPT_M_ZIP) + if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD) { // Generate a seed and store it in the memfile. sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0); } +#ifdef FEAT_SODIUM + else if (method_nr == CRYPT_M_SOD) + randombytes_buf(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN); + #endif } } @@ -447,7 +458,7 @@ ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p) int method_nr = crypt_get_method_nr(buf); b0p->b0_id[1] = id1_codes[method_nr]; - if (method_nr > CRYPT_M_ZIP) + if (method_nr > CRYPT_M_ZIP && method_nr < CRYPT_M_SOD) { // Generate a seed and store it in block 0 and in the memfile. sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0); @@ -482,10 +493,18 @@ ml_set_crypt_key( int top; int old_method; - if (mfp == NULL) + if (mfp == NULL || mfp->mf_fd < 0) return; // no memfile yet, nothing to do old_method = crypt_method_nr_from_name(old_cm); + // Swapfile encryption not supported by XChaCha20 + if (crypt_get_method_nr(buf) == CRYPT_M_SOD && *buf->b_p_key != NUL) + { + // close the swapfile + mf_close_file(buf, TRUE); + buf->b_p_swf = FALSE; + return; + } // First make sure the swapfile is in a consistent state, using the old // key and method. { @@ -911,7 +930,8 @@ ml_check_b0_id(ZERO_BL *b0p) || (b0p->b0_id[1] != BLOCK0_ID1 && b0p->b0_id[1] != BLOCK0_ID1_C0 && b0p->b0_id[1] != BLOCK0_ID1_C1 - && b0p->b0_id[1] != BLOCK0_ID1_C2) + && b0p->b0_id[1] != BLOCK0_ID1_C2 + && b0p->b0_id[1] != BLOCK0_ID1_C3) ) return FAIL; return OK; @@ -2402,7 +2422,9 @@ ml_sync_all(int check_file, int check_char) FOR_ALL_BUFFERS(buf) { - if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL) + if (buf->b_ml.ml_mfp == NULL + || buf->b_ml.ml_mfp->mf_fname == NULL + || buf->b_ml.ml_mfp->mf_fd < 0) continue; // no file ml_flush_line(buf); // flush buffered line @@ -5320,7 +5342,8 @@ ml_encrypt_data( mch_memmove(new_data, dp, head_end - (char_u *)dp); // Encrypt the text. - crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start); + crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start, + FALSE); crypt_free_state(state); // Clear the gap. @@ -5360,7 +5383,7 @@ ml_decrypt_data( if (state != NULL) { // Decrypt the text in place. - crypt_decode_inplace(state, text_start, text_len); + crypt_decode_inplace(state, text_start, text_len, FALSE); crypt_free_state(state); } } @@ -5407,7 +5430,7 @@ ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading) // of the block for the salt. vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset); return crypt_create(method_nr, key, salt, (int)STRLEN(salt), - seed, MF_SEED_LEN); + seed, MF_SEED_LEN); } #endif diff --git a/src/option.c b/src/option.c index 712590ef3f..f14c8fb747 100644 --- a/src/option.c +++ b/src/option.c @@ -1324,9 +1324,10 @@ do_set( // remember character after option name afterchar = arg[len]; - // skip white space, allow ":set ai ?" - while (VIM_ISWHITE(arg[len])) - ++len; + if (!in_vim9script()) + // skip white space, allow ":set ai ?", ":set hlsearch !" + while (VIM_ISWHITE(arg[len])) + ++len; adding = FALSE; prepending = FALSE; @@ -2734,6 +2735,10 @@ set_bool_option( || (opt_flags & OPT_GLOBAL) || opt_flags == 0) && !curbufIsChanged() && curbuf->b_ml.ml_mfp != NULL) { +#ifdef FEAT_CRYPT + if (crypt_get_method_nr(curbuf) == CRYPT_M_SOD) + continue; +#endif u_compute_hash(hash); u_read_undo(NULL, hash, curbuf->b_fname); } diff --git a/src/optionstr.c b/src/optionstr.c index cc56afe63c..0c6c983826 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -24,7 +24,11 @@ static char *(p_bo_values[]) = {"all", "backspace", "cursor", "complete", static char *(p_nf_values[]) = {"bin", "octal", "hex", "alpha", "unsigned", NULL}; static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL}; #ifdef FEAT_CRYPT -static char *(p_cm_values[]) = {"zip", "blowfish", "blowfish2", NULL}; +static char *(p_cm_values[]) = {"zip", "blowfish", "blowfish2", + # ifdef FEAT_SODIUM + "xchacha20", + # endif + NULL}; #endif static char *(p_cmp_values[]) = {"internal", "keepascii", NULL}; static char *(p_dy_values[]) = {"lastline", "truncate", "uhex", NULL}; diff --git a/src/proto/blowfish.pro b/src/proto/blowfish.pro index 5998fb3581..6b2c45459a 100644 --- a/src/proto/blowfish.pro +++ b/src/proto/blowfish.pro @@ -1,6 +1,6 @@ /* blowfish.c */ -void crypt_blowfish_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to); -void crypt_blowfish_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to); +void crypt_blowfish_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); +void crypt_blowfish_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); int crypt_blowfish_init(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len); int blowfish_self_test(void); /* vim: set ft=c : */ diff --git a/src/proto/crypt.pro b/src/proto/crypt.pro index e8ba18897c..18382a7f3e 100644 --- a/src/proto/crypt.pro +++ b/src/proto/crypt.pro @@ -1,9 +1,11 @@ /* crypt.c */ int crypt_method_nr_from_name(char_u *name); int crypt_method_nr_from_magic(char *ptr, int len); +int crypt_works_inplace(cryptstate_T *state); int crypt_get_method_nr(buf_T *buf); int crypt_whole_undofile(int method_nr); int crypt_get_header_len(int method_nr); +int crypt_get_max_header_len(void); void crypt_set_cm_option(buf_T *buf, int method_nr); int crypt_self_test(void); cryptstate_T *crypt_create(int method_nr, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len); @@ -11,12 +13,17 @@ cryptstate_T *crypt_create_from_header(int method_nr, char_u *key, char_u *heade cryptstate_T *crypt_create_from_file(FILE *fp, char_u *key); cryptstate_T *crypt_create_for_writing(int method_nr, char_u *key, char_u **header, int *header_len); void crypt_free_state(cryptstate_T *state); -void crypt_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to); -void crypt_encode_inplace(cryptstate_T *state, char_u *buf, size_t len); -void crypt_decode_inplace(cryptstate_T *state, char_u *buf, size_t len); +long crypt_encode_alloc(cryptstate_T *state, char_u *from, size_t len, char_u **newptr, int last); +long crypt_decode_alloc(cryptstate_T *state, char_u *ptr, long len, char_u **newptr, int last); +void crypt_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); +void crypt_encode_inplace(cryptstate_T *state, char_u *buf, size_t len, int last); +void crypt_decode_inplace(cryptstate_T *state, char_u *buf, size_t len, int last); void crypt_free_key(char_u *key); void crypt_check_method(int method); void crypt_check_current_method(void); char_u *crypt_get_key(int store, int twice); void crypt_append_msg(buf_T *buf); +int crypt_sodium_init(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len); +long crypt_sodium_buffer_encode(cryptstate_T *state, char_u *from, size_t len, char_u **buf_out, int last); +long crypt_sodium_buffer_decode(cryptstate_T *state, char_u *from, size_t len, char_u **buf_out, int last); /* vim: set ft=c : */ diff --git a/src/proto/crypt_zip.pro b/src/proto/crypt_zip.pro index 868b131f0f..626d9855bf 100644 --- a/src/proto/crypt_zip.pro +++ b/src/proto/crypt_zip.pro @@ -1,5 +1,5 @@ /* crypt_zip.c */ int crypt_zip_init(cryptstate_T *state, char_u *key, char_u *salt, int salt_len, char_u *seed, int seed_len); -void crypt_zip_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to); -void crypt_zip_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to); +void crypt_zip_encode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); +void crypt_zip_decode(cryptstate_T *state, char_u *from, size_t len, char_u *to, int last); /* vim: set ft=c : */ diff --git a/src/proto/testing.pro b/src/proto/testing.pro index d229030cb5..3e203f8fd6 100644 --- a/src/proto/testing.pro +++ b/src/proto/testing.pro @@ -34,5 +34,6 @@ void f_test_unknown(typval_T *argvars, typval_T *rettv); void f_test_void(typval_T *argvars, typval_T *rettv); void f_test_scrollbar(typval_T *argvars, typval_T *rettv); void f_test_setmouse(typval_T *argvars, typval_T *rettv); +void f_test_gui_mouse_event(typval_T *argvars, typval_T *rettv); void f_test_settime(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/quickfix.c b/src/quickfix.c index ad07a5b4ee..36574b80f7 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -30,13 +30,16 @@ struct qfline_S qfline_T *qf_next; // pointer to next error in the list qfline_T *qf_prev; // pointer to previous error in the list linenr_T qf_lnum; // line number where the error occurred + linenr_T qf_end_lnum; // line number when the error has range or zero int qf_fnum; // file number for the line int qf_col; // column where the error occurred + int qf_end_col; // column when the error has range or zero int qf_nr; // error number char_u *qf_module; // module name for this error char_u *qf_pattern; // search pattern for the error char_u *qf_text; // description of the error - char_u qf_viscol; // set to TRUE if qf_col is screen column + char_u qf_viscol; // set to TRUE if qf_col and qf_end_col is + // screen column char_u qf_cleared; // set to TRUE if line has been deleted char_u qf_type; // type of the error (mostly 'E'); 1 for // :helpgrep @@ -165,7 +168,7 @@ static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls static callback_T qftf_cb; static void qf_new_list(qf_info_T *qi, char_u *qf_title); -static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *module, int bufnum, char_u *mesg, long lnum, int col, int vis_col, char_u *pattern, int nr, int type, int valid); +static int qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *module, int bufnum, char_u *mesg, long lnum, long end_lnum, int col, int end_col, int vis_col, char_u *pattern, int nr, int type, int valid); static void qf_free(qf_list_T *qfl); static char_u *qf_types(int, int); static int qf_get_fnum(qf_list_T *qfl, char_u *, char_u *); @@ -174,6 +177,7 @@ static char_u *qf_pop_dir(struct dir_stack_T **); static char_u *qf_guess_filepath(qf_list_T *qfl, char_u *); static void qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, int newwin); static void qf_fmt_text(char_u *text, char_u *buf, int bufsize); +static void qf_range_text(qfline_T *qfp, char_u *buf, int bufsize); static int qf_win_pos_update(qf_info_T *qi, int old_qf_index); static win_T *qf_find_win(qf_info_T *qi); static buf_T *qf_find_buf(qf_info_T *qi); @@ -899,7 +903,9 @@ typedef struct { char_u *errmsg; int errmsglen; long lnum; + long end_lnum; int col; + int end_col; char_u use_viscol; char_u *pattern; int enr; @@ -1235,7 +1241,9 @@ qf_parse_get_fields( if (!qf_multiscan) fields->errmsg[0] = NUL; fields->lnum = 0; + fields->end_lnum = 0; fields->col = 0; + fields->end_col = 0; fields->use_viscol = FALSE; fields->enr = -1; fields->type = 0; @@ -1630,7 +1638,9 @@ qf_init_process_nextline( 0, fields->errmsg, fields->lnum, + fields->end_lnum, fields->col, + fields->end_col, fields->use_viscol, fields->pattern, fields->enr, @@ -2053,7 +2063,9 @@ qf_add_entry( int bufnum, // buffer number or zero char_u *mesg, // message long lnum, // line number + long end_lnum, // line number for end int col, // column + int end_col, // column for end int vis_col, // using visual column char_u *pattern, // search pattern int nr, // error number @@ -2082,7 +2094,9 @@ qf_add_entry( return QF_FAIL; } qfp->qf_lnum = lnum; + qfp->qf_end_lnum = end_lnum; qfp->qf_col = col; + qfp->qf_end_col = end_col; qfp->qf_viscol = vis_col; if (pattern == NULL || *pattern == NUL) qfp->qf_pattern = NULL; @@ -2239,7 +2253,9 @@ copy_loclist_entries(qf_list_T *from_qfl, qf_list_T *to_qfl) 0, from_qfp->qf_text, from_qfp->qf_lnum, + from_qfp->qf_end_lnum, from_qfp->qf_col, + from_qfp->qf_end_col, from_qfp->qf_viscol, from_qfp->qf_pattern, from_qfp->qf_nr, @@ -3555,11 +3571,8 @@ qf_list_entry(qfline_T *qfp, int qf_idx, int cursel) msg_puts_attr(":", qfSepAttr); if (qfp->qf_lnum == 0) IObuff[0] = NUL; - else if (qfp->qf_col == 0) - sprintf((char *)IObuff, "%ld", qfp->qf_lnum); else - sprintf((char *)IObuff, "%ld col %d", - qfp->qf_lnum, qfp->qf_col); + qf_range_text(qfp, IObuff, IOSIZE); sprintf((char *)IObuff + STRLEN(IObuff), "%s", (char *)qf_types(qfp->qf_type, qfp->qf_nr)); msg_puts_attr((char *)IObuff, qfLineAttr); @@ -3685,6 +3698,37 @@ qf_fmt_text(char_u *text, char_u *buf, int bufsize) buf[i] = NUL; } +/* + * Range information from lnum, col, end_lnum, and end_col. + * Put the result in "buf[bufsize]". + */ + static void +qf_range_text(qfline_T *qfp, char_u *buf, int bufsize) +{ + int len; + vim_snprintf((char *)buf, bufsize, "%ld", qfp->qf_lnum); + len = (int)STRLEN(buf); + + if (qfp->qf_end_lnum > 0 && qfp->qf_lnum != qfp->qf_end_lnum) + { + vim_snprintf((char *)buf + len, bufsize - len, + "-%ld", qfp->qf_end_lnum); + len += (int)STRLEN(buf + len); + } + if (qfp->qf_col > 0) + { + vim_snprintf((char *)buf + len, bufsize - len, " col %d", qfp->qf_col); + len += (int)STRLEN(buf + len); + if (qfp->qf_end_col > 0 && qfp->qf_col != qfp->qf_end_col) + { + vim_snprintf((char *)buf + len, bufsize - len, + "-%d", qfp->qf_end_col); + len += (int)STRLEN(buf + len); + } + } + buf[len] = NUL; +} + /* * Display information (list number, list size and the title) about a * quickfix/location list. @@ -4473,7 +4517,17 @@ qf_update_buffer(qf_info_T *qi, qfline_T *old_last) int qf_winid = 0; if (IS_LL_STACK(qi)) - qf_winid = curwin->w_id; + { + if (curwin->w_llist == qi) + win = curwin; + else + { + win = qf_find_win_with_loclist(qi); + if (win == NULL) + return; + } + qf_winid = win->w_id; + } if (old_last == NULL) // set curwin/curbuf to buf and save a few things @@ -4555,17 +4609,9 @@ qf_buf_add_line( if (qfp->qf_lnum > 0) { - vim_snprintf((char *)IObuff + len, IOSIZE - len, "%ld", - qfp->qf_lnum); + qf_range_text(qfp, IObuff + len, IOSIZE - len); len += (int)STRLEN(IObuff + len); - if (qfp->qf_col > 0) - { - vim_snprintf((char *)IObuff + len, IOSIZE - len, - " col %d", qfp->qf_col); - len += (int)STRLEN(IObuff + len); - } - vim_snprintf((char *)IObuff + len, IOSIZE - len, "%s", (char *)qf_types(qfp->qf_type, qfp->qf_nr)); len += (int)STRLEN(IObuff + len); @@ -5943,7 +5989,9 @@ vgr_match_buflines( ml_get_buf(buf, regmatch->startpos[0].lnum + lnum, FALSE), regmatch->startpos[0].lnum + lnum, + regmatch->endpos[0].lnum + lnum, regmatch->startpos[0].col + 1, + regmatch->endpos[0].col + 1, FALSE, // vis_col NULL, // search pattern 0, // nr @@ -5986,7 +6034,9 @@ vgr_match_buflines( duplicate_name ? 0 : buf->b_fnum, str, lnum, + 0, matches[0] + col + 1, + 0, FALSE, // vis_col NULL, // search pattern 0, // nr @@ -6616,10 +6666,12 @@ get_qfline_items(qfline_T *qfp, list_T *list) buf[0] = qfp->qf_type; buf[1] = NUL; if (dict_add_number(dict, "bufnr", (long)bufnum) == FAIL - || dict_add_number(dict, "lnum", (long)qfp->qf_lnum) == FAIL - || dict_add_number(dict, "col", (long)qfp->qf_col) == FAIL - || dict_add_number(dict, "vcol", (long)qfp->qf_viscol) == FAIL - || dict_add_number(dict, "nr", (long)qfp->qf_nr) == FAIL + || dict_add_number(dict, "lnum", (long)qfp->qf_lnum) == FAIL + || dict_add_number(dict, "end_lnum", (long)qfp->qf_end_lnum) == FAIL + || dict_add_number(dict, "col", (long)qfp->qf_col) == FAIL + || dict_add_number(dict, "end_col", (long)qfp->qf_end_col) == FAIL + || dict_add_number(dict, "vcol", (long)qfp->qf_viscol) == FAIL + || dict_add_number(dict, "nr", (long)qfp->qf_nr) == FAIL || dict_add_string(dict, "module", qfp->qf_module) == FAIL || dict_add_string(dict, "pattern", qfp->qf_pattern) == FAIL || dict_add_string(dict, "text", qfp->qf_text) == FAIL @@ -7133,8 +7185,8 @@ qf_add_entry_from_dict( { static int did_bufnr_emsg; char_u *filename, *module, *pattern, *text, *type; - int bufnum, valid, status, col, vcol, nr; - long lnum; + int bufnum, valid, status, col, end_col, vcol, nr; + long lnum, end_lnum; if (first_entry) did_bufnr_emsg = FALSE; @@ -7143,7 +7195,9 @@ qf_add_entry_from_dict( module = dict_get_string(d, (char_u *)"module", TRUE); bufnum = (int)dict_get_number(d, (char_u *)"bufnr"); lnum = (int)dict_get_number(d, (char_u *)"lnum"); + end_lnum = (int)dict_get_number(d, (char_u *)"end_lnum"); col = (int)dict_get_number(d, (char_u *)"col"); + end_col = (int)dict_get_number(d, (char_u *)"end_col"); vcol = (int)dict_get_number(d, (char_u *)"vcol"); nr = (int)dict_get_number(d, (char_u *)"nr"); type = dict_get_string(d, (char_u *)"type", TRUE); @@ -7180,7 +7234,9 @@ qf_add_entry_from_dict( bufnum, text, lnum, + end_lnum, col, + end_col, vcol, // vis_col pattern, // search pattern nr, @@ -8048,8 +8104,11 @@ hgr_search_file( 0, line, lnum, + 0, (int)(p_regmatch->startp[0] - line) + 1, // col + (int)(p_regmatch->endp[0] - line) + + 1, // end_col FALSE, // vis_col NULL, // search pattern 0, // nr diff --git a/src/register.c b/src/register.c index c30787afef..4774e2a995 100644 --- a/src/register.c +++ b/src/register.c @@ -462,6 +462,9 @@ stuff_yank(int regname, char_u *p) return OK; } +/* + * Last executed register (@ command) + */ static int execreg_lastc = NUL; int diff --git a/src/structs.h b/src/structs.h index 43aeac1ae9..31a2e57596 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1629,6 +1629,11 @@ typedef struct # endif garray_T uf_lines; // function lines + + int uf_debug_tick; // when last checked for a breakpoint in this + // function. + int uf_has_breakpoint; // TRUE when a breakpoint has been set in + // this function. # ifdef FEAT_PROFILE int uf_profiling; // TRUE when func is being profiled int uf_prof_initialized; @@ -2516,11 +2521,12 @@ typedef struct { # define CRYPT_M_ZIP 0 # define CRYPT_M_BF 1 # define CRYPT_M_BF2 2 -# define CRYPT_M_COUNT 3 // number of crypt methods +# define CRYPT_M_SOD 3 +# define CRYPT_M_COUNT 4 // number of crypt methods // Currently all crypt methods work inplace. If one is added that isn't then // define this. -// # define CRYPT_NOT_INPLACE 1 +# define CRYPT_NOT_INPLACE 1 #endif #ifdef FEAT_PROP_POPUP diff --git a/src/testdir/dumps/Test_quickfix_cwindow_1.dump b/src/testdir/dumps/Test_quickfix_cwindow_1.dump index ef153f9089..c2b487232e 100644 --- a/src/testdir/dumps/Test_quickfix_cwindow_1.dump +++ b/src/testdir/dumps/Test_quickfix_cwindow_1.dump @@ -4,9 +4,9 @@ |m|a|t|c|h|e|s| @67 |~+0#4040ff13&| @73 |X+1#0000000&|C|w|i|n|d|o|w| @48|1|,|4| @11|A|l@1 ->X+0#0000e05#ffff4012|C|w|i|n|d|o|w||+0#0000000&|1+0#af5f00255&| |c|o|l| |4||+0#0000000&| |s|o|m|e| @52 -|X+0#0000e05#ffffff0|C|w|i|n|d|o|w||+0#0000000&|2+0#af5f00255&| |c|o|l| |2||+0#0000000&| |t|e|x|t| @52 -|X+0#0000e05&|C|w|i|n|d|o|w||+0#0000000&|4+0#af5f00255&| |c|o|l| |6||+0#0000000&| |m|a|t|c|h|e|s| @49 +>X+0#0000e05#ffff4012|C|w|i|n|d|o|w||+0#0000000&|1+0#af5f00255&| |c|o|l| |4|-|5||+0#0000000&| |s|o|m|e| @50 +|X+0#0000e05#ffffff0|C|w|i|n|d|o|w||+0#0000000&|2+0#af5f00255&| |c|o|l| |2|-|3||+0#0000000&| |t|e|x|t| @50 +|X+0#0000e05&|C|w|i|n|d|o|w||+0#0000000&|4+0#af5f00255&| |c|o|l| |6|-|7||+0#0000000&| |m|a|t|c|h|e|s| @47 |~+0#4040ff13&| @73 |[+3#0000000&|Q|u|i|c|k|f|i|x| |L|i|s|t|]| |:|v|i|m|g|r|e|p| |e| |X|C|w|i|n|d|o|w| @20|1|,|1| @12|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_quickfix_cwindow_2.dump b/src/testdir/dumps/Test_quickfix_cwindow_2.dump index 4c87ca053f..0e7fa3c9ee 100644 --- a/src/testdir/dumps/Test_quickfix_cwindow_2.dump +++ b/src/testdir/dumps/Test_quickfix_cwindow_2.dump @@ -4,9 +4,9 @@ |m|a|t|c|h|e|s| @67 |~+0#4040ff13&| @73 |X+3#0000000&|C|w|i|n|d|o|w| @48|2|,|2| @11|A|l@1 -|X+0#0000e05&|C|w|i|n|d|o|w||+0#0000000&|1+0#af5f00255&| |c|o|l| |4||+0#0000000&| |s|o|m|e| @52 -|X+0#0000e05#ffff4012|C|w|i|n|d|o|w||+0#0000000&|2+0#af5f00255&| |c|o|l| |2||+0#0000000&| |t|e|x|t| @52 -|X+0#0000e05#ffffff0|C|w|i|n|d|o|w||+0#0000000&|4+0#af5f00255&| |c|o|l| |6||+0#0000000&| |m|a|t|c|h|e|s| @49 +|X+0#0000e05&|C|w|i|n|d|o|w||+0#0000000&|1+0#af5f00255&| |c|o|l| |4|-|5||+0#0000000&| |s|o|m|e| @50 +|X+0#0000e05#ffff4012|C|w|i|n|d|o|w||+0#0000000&|2+0#af5f00255&| |c|o|l| |2|-|3||+0#0000000&| |t|e|x|t| @50 +|X+0#0000e05#ffffff0|C|w|i|n|d|o|w||+0#0000000&|4+0#af5f00255&| |c|o|l| |6|-|7||+0#0000000&| |m|a|t|c|h|e|s| @47 |~+0#4040ff13&| @73 |[+1#0000000&|Q|u|i|c|k|f|i|x| |L|i|s|t|]| |:|v|i|m|g|r|e|p| |e| |X|C|w|i|n|d|o|w| @20|2|,|1| @12|A|l@1 |:+0&&|c|n|e|x|t| @68 diff --git a/src/testdir/samples/crypt_sodium_invalid.txt b/src/testdir/samples/crypt_sodium_invalid.txt new file mode 100644 index 0000000000..35e31b5ac9 Binary files /dev/null and b/src/testdir/samples/crypt_sodium_invalid.txt differ diff --git a/src/testdir/test_assert.vim b/src/testdir/test_assert.vim index 04bd87729f..e0dc99e7c6 100644 --- a/src/testdir/test_assert.vim +++ b/src/testdir/test_assert.vim @@ -374,6 +374,8 @@ func Test_mouse_position() call test_setmouse(5, 1) call feedkeys("\", "xt") call assert_equal([0, 2, 1, 0], getpos('.')) + call assert_fails('call test_setmouse("", 2)', 'E474:') + call assert_fails('call test_setmouse(1, "")', 'E474:') bwipe! let &mouse = save_mouse endfunc diff --git a/src/testdir/test_crypt.vim b/src/testdir/test_crypt.vim index f386593cec..63bf8a5b25 100644 --- a/src/testdir/test_crypt.vim +++ b/src/testdir/test_crypt.vim @@ -22,6 +22,11 @@ func Test_head_only_3() call Common_head_only('VimCrypt~03!abc') endfunc +func Test_head_only_4() + CheckFeature sodium + call Common_head_only('VimCrypt~04!abc') +endfunc + func Crypt_uncrypt(method) exe "set cryptmethod=" . a:method " If the blowfish test fails 'cryptmethod' will be 'zip' now. @@ -55,6 +60,11 @@ func Test_crypt_blowfish2() call Crypt_uncrypt('blowfish2') endfunc +func Test_crypt_sodium() + CheckFeature sodium + call Crypt_uncrypt('xchacha20') +endfunc + func Uncrypt_stable(method, crypted_text, key, uncrypted_text) split Xtest.txt set bin noeol key= fenc=latin1 @@ -70,6 +80,16 @@ func Uncrypt_stable(method, crypted_text, key, uncrypted_text) set key= endfunc +func Uncrypt_stable_xxd(method, hex, key, uncrypted_text) + " use xxd to write the binary content + call system('xxd -r >Xtest.txt', a:hex) + call feedkeys(":split Xtest.txt\" . a:key . "\", 'xt') + call assert_equal(a:uncrypted_text, getline(1, len(a:uncrypted_text))) + bwipe! + call delete('Xtest.txt') + set key= +endfunc + func Test_uncrypt_zip() call Uncrypt_stable('zip', "VimCrypt~01!\u0006\u001clV'\u00de}Mg\u00a0\u00ea\u00a3V\u00a9\u00e7\u0007E#3\u008e2U\u00e9\u0097", "foofoo", ["1234567890", "aábbccddeëff"]) endfunc @@ -78,10 +98,115 @@ func Test_uncrypt_blowfish() call Uncrypt_stable('blowfish', "VimCrypt~02!k)\u00be\u0017\u0097#\u0016\u00ddS\u009c\u00f5=\u00ba\u00e0\u00c8#\u00a5M\u00b4\u0086J\u00c3A\u00cd\u00a5M\u00b4\u0086!\u0080\u0015\u009b\u00f5\u000f\u00e1\u00d2\u0019\u0082\u0016\u0098\u00f7\u000d\u00da", "barbar", ["asdfasdfasdf", "0001112223333"]) endfunc -func Test_uncrypt_blowfish2() +func Test_uncrypt_blowfish2a() call Uncrypt_stable('blowfish', "VimCrypt~03!\u001e\u00d1N\u00e3;\u00d3\u00c0\u00a0^C)\u0004\u00f7\u007f.\u00b6\u00abF\u000eS\u0019\u00e0\u008b6\u00d2[T\u00cb\u00a7\u0085\u00d8\u00be9\u000b\u00812\u000bQ\u00b3\u00cc@\u0097\u000f\u00df\u009a\u00adIv\u00aa.\u00d8\u00c9\u00ee\u009e`\u00bd$\u00af%\u00d0", "barburp", ["abcdefghijklmnopqrstuvwxyz", "!@#$%^&*()_+=-`~"]) endfunc +func Test_uncrypt_blowfish2() + call Uncrypt_stable('blowfish2', "VimCrypt~03!\u001e\u00d1N\u00e3;\u00d3\u00c0\u00a0^C)\u0004\u00f7\u007f.\u00b6\u00abF\u000eS\u0019\u00e0\u008b6\u00d2[T\u00cb\u00a7\u0085\u00d8\u00be9\u000b\u00812\u000bQ\u00b3\u00cc@\u0097\u000f\u00df\u009a\u00adIv\u00aa.\u00d8\u00c9\u00ee\u009e`\u00bd$\u00af%\u00d0", "barburp", ["abcdefghijklmnopqrstuvwxyz", "!@#$%^&*()_+=-`~"]) +endfunc + +func Test_uncrypt_xchacha20() + CheckFeature sodium + let hex=['00000000: 5669 6d43 7279 7074 7e30 3421 6b7d e607 vimCrypt~04!k}..', + \ '00000010: 4ea4 e99f 923e f67f 7b59 a80d 3bca 2f06 N....>..{Y..;./.', + \ '00000020: fa11 b951 8d09 0dc9 470f e7cf 8b90 4310 ...Q....G.....C.', + \ '00000030: 653b b83b e493 378b 0390 0e38 f912 626b e;.;..7....8..bk', + \ '00000040: a02e 4697 0254 2625 2d8e 3a0b 784b e89c ..F..T&%-.:.xK..', + \ '00000050: 0c67 a975 3c17 9319 8ffd 1463 7783 a1f3 .g.u<......cw...', + \ '00000060: d917 dcb3 8b3e ecd7 c7d4 086b 6059 7ead .....>.....k`Y~.', + \ '00000070: 9b07 f96b 5c1b 4d08 cd91 f208 5221 7484 ...k\.M.....R!t.', + \ '00000080: 72be 0136 84a1 d3 r..6...'] + " the file should be in latin1 encoding, this makes sure that readfile() + " retries several times converting the multi-byte characters + call Uncrypt_stable_xxd('xchacha20', hex, "sodium_crypt", ["abcdefghijklmnopqrstuvwxyzäöü", "ZZZ_äüöÄÜÖ_!@#$%^&*()_+=-`~"]) +endfunc + +func Test_uncrypt_xchacha20_invalid() + CheckFeature sodium + " load an invalid encrypted file and verify it can be decrypted with an + " error message + try + call feedkeys(":split samples/crypt_sodium_invalid.txt\sodium\", 'xt') + call assert_false(1, 'should not happen') + catch + call assert_exception('pre-mature') + endtry + call assert_match("Note: Encryption of swapfile not supported, disabling swap- and undofile", execute(':5messages')) + + call assert_equal(0, &swapfile) + call assert_equal("xchacha20", &cryptmethod) + call assert_equal('311111111111111111111111', getline('$')) + bw! +endfunc + +func Test_uncrypt_xchacha20_2() + CheckFeature sodium + sp Xcrypt_sodium.txt + " Create a larger file, so that Vim will write in several blocks + call setline(1, range(1,4000)) + call assert_equal(1, &swapfile) + set cryptmethod=xchacha20 + call feedkeys(":X\sodium\sodium\", 'xt') + " swapfile disabled + call assert_equal(0, &swapfile) + call assert_match("Note: Encryption of swapfile not supported, disabling swap- and undofile", execute(':messages')) + w! + " encrypted using xchacha20 + call assert_match("\[xchacha20\]", execute(':messages')) + bw! + call feedkeys(":sp Xcrypt_sodium.txt\sodium\", 'xt') + " successfully decrypted + call assert_equal(range(1, 4000)->map( {_, v -> string(v)}), getline(1,'$')) + set key= + w! + " enryption removed + call assert_match('"Xcrypt_sodium.txt" 4000L, 18893B written', execute(':message')) + bw! + call delete('Xcrypt_sodium.txt') + set cryptmethod&vim +endfunc + +func Test_uncrypt_xchacha20_3_persistent_undo() + CheckFeature sodium + CheckFeature persistent_undo + sp Xcrypt_sodium_undo.txt + set cryptmethod=xchacha20 undofile + call feedkeys(":X\sodium\sodium\", 'xt') + call assert_equal(0, &undofile) + let ufile=undofile(@%) + call append(0, ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']) + call cursor(1, 1) + + set undolevels=100 + normal dd + set undolevels=100 + normal dd + set undolevels=100 + normal dd + set undolevels=100 + w! + bw! + call feedkeys(":sp Xcrypt_sodium_undo.txt\sodium\", 'xt') + " should fail + norm! u + call assert_match('Already at oldest change', execute(':1mess')) + call assert_fails('verbose rundo' .. fnameescape(ufile), 'E822') + bw! + set undolevels& cryptmethod& undofile& + call delete('Xcrypt_sodium_undo.txt') +endfunc + +func Test_encrypt_xchacha20_missing() + if has("sodium") + return + endif + sp Xcrypt_sodium_undo.txt + call assert_fails(':set cryptmethod=xchacha20', 'E474') + bw! + set cm& +endfunc + func Test_uncrypt_unknown_method() split Xuncrypt_unknown.txt set bin noeol key= fenc=latin1 diff --git a/src/testdir/test_debugger.vim b/src/testdir/test_debugger.vim index bbf74258b2..81e86588cc 100644 --- a/src/testdir/test_debugger.vim +++ b/src/testdir/test_debugger.vim @@ -885,19 +885,19 @@ func Test_Backtrace_DefFunction() \ ':debug call GlobalFunction()', \ ['cmd: call GlobalFunction()']) - call RunDbgCmd(buf, 'step', ['line 1: var some = "some var"']) - call RunDbgCmd(buf, 'step', ['line 2: CallAFunction()']) + call RunDbgCmd(buf, 'step', ['line 1: var some = "some var"']) + call RunDbgCmd(buf, 'step', ['line 2: CallAFunction()']) call RunDbgCmd(buf, 'echo some', ['some var']) call RunDbgCmd(buf, 'backtrace', [ \ '\V>backtrace', \ '\V->0 function GlobalFunction', - \ '\Vline 2: CallAFunction()', + \ '\Vline 2: CallAFunction()', \ ], \ #{match: 'pattern'}) - call RunDbgCmd(buf, 'step', ['line 1: SourceAnotherFile()']) - call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) + call RunDbgCmd(buf, 'step', ['line 1: SourceAnotherFile()']) + call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) " Repeated line, because we fist are in the compiled function before the " EXEC and then in do_cmdline() before the :source command. call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) @@ -932,17 +932,83 @@ func Test_Backtrace_DefFunction() call delete('Xtest2.vim') endfunc +func Test_debug_DefFunction() + CheckCWD + let file =<< trim END + vim9script + def g:SomeFunc() + echo "here" + echo "and" + echo "there" + breakadd func 2 LocalFunc + LocalFunc() + enddef + + def LocalFunc() + echo "first" + echo "second" + breakadd func 1 LegacyFunc + LegacyFunc() + enddef + + func LegacyFunc() + echo "legone" + echo "legtwo" + endfunc + + breakadd func 2 g:SomeFunc + END + call writefile(file, 'XtestDebug.vim') + + let buf = RunVimInTerminal('-S XtestDebug.vim', {}) + + call RunDbgCmd(buf,':call SomeFunc()', ['line 2: echo "and"']) + call RunDbgCmd(buf,'next', ['line 3: echo "there"']) + call RunDbgCmd(buf,'next', ['line 4: breakadd func 2 LocalFunc']) + + " continue, next breakpoint is in LocalFunc() + call RunDbgCmd(buf,'cont', ['line 2: echo "second"']) + + " continue, next breakpoint is in LegacyFunc() + call RunDbgCmd(buf,'cont', ['line 1: echo "legone"']) + + call RunDbgCmd(buf, 'cont') + + call StopVimInTerminal(buf) + call delete('Xtest1.vim') + call delete('Xtest2.vim') +endfunc + func Test_debug_def_function() CheckCWD let file =<< trim END vim9script def g:Func() - var n: number - def Closure(): number - return n + 3 - enddef - n += Closure() - echo 'result: ' .. n + var n: number + def Closure(): number + return n + 3 + enddef + n += Closure() + echo 'result: ' .. n + enddef + + def g:FuncWithArgs(text: string, nr: number, ...items: list) + echo text .. nr + for it in items + echo it + endfor + echo "done" + enddef + + def g:FuncWithDict() + var d = { + a: 1, + b: 2, + } + # comment + def Inner() + eval 1 + enddef enddef END call writefile(file, 'Xtest.vim') @@ -954,7 +1020,37 @@ func Test_debug_def_function() \ ['cmd: call Func()']) call RunDbgCmd(buf, 'next', ['result: 3']) call term_sendkeys(buf, "\r") + call RunDbgCmd(buf, 'cont') + call RunDbgCmd(buf, + \ ':debug call FuncWithArgs("asdf", 42, 1, 2, 3)', + \ ['cmd: call FuncWithArgs("asdf", 42, 1, 2, 3)']) + call RunDbgCmd(buf, 'step', ['line 1: echo text .. nr']) + call RunDbgCmd(buf, 'echo text', ['asdf']) + call RunDbgCmd(buf, 'echo nr', ['42']) + call RunDbgCmd(buf, 'echo items', ['[1, 2, 3]']) + call RunDbgCmd(buf, 'step', ['asdf42', 'function FuncWithArgs', 'line 2: for it in items']) + call RunDbgCmd(buf, 'echo it', ['1']) + call RunDbgCmd(buf, 'step', ['line 3: echo it']) + call RunDbgCmd(buf, 'step', ['1', 'function FuncWithArgs', 'line 4: endfor']) + call RunDbgCmd(buf, 'step', ['line 2: for it in items']) + call RunDbgCmd(buf, 'echo it', ['2']) + call RunDbgCmd(buf, 'step', ['line 3: echo it']) + call RunDbgCmd(buf, 'step', ['2', 'function FuncWithArgs', 'line 4: endfor']) + call RunDbgCmd(buf, 'step', ['line 2: for it in items']) + call RunDbgCmd(buf, 'echo it', ['3']) + call RunDbgCmd(buf, 'step', ['line 3: echo it']) + call RunDbgCmd(buf, 'step', ['3', 'function FuncWithArgs', 'line 4: endfor']) + call RunDbgCmd(buf, 'step', ['line 5: echo "done"']) + call RunDbgCmd(buf, 'cont') + + call RunDbgCmd(buf, + \ ':debug call FuncWithDict()', + \ ['cmd: call FuncWithDict()']) + call RunDbgCmd(buf, 'step', ['line 1: var d = { a: 1, b: 2, }']) + call RunDbgCmd(buf, 'step', ['line 6: def Inner()']) + + call RunDbgCmd(buf, 'cont') call StopVimInTerminal(buf) call delete('Xtest.vim') endfunc diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim index 0c1e75df59..1fbb74bc4e 100644 --- a/src/testdir/test_eval_stuff.vim +++ b/src/testdir/test_eval_stuff.vim @@ -165,7 +165,7 @@ func Test_string_concat_scriptversion2() call assert_fails('echo a . b', 'E15:') call assert_fails('let a .= b', 'E985:') - call assert_fails('let vers = 1.2.3', 'E15:') + call assert_fails('let vers = 1.2.3', 'E488:') if has('float') let f = .5 diff --git a/src/testdir/test_execute_func.vim b/src/testdir/test_execute_func.vim index c08b2390f2..aa73e0f317 100644 --- a/src/testdir/test_execute_func.vim +++ b/src/testdir/test_execute_func.vim @@ -40,7 +40,7 @@ func Test_execute_string() if has('float') call assert_fails('call execute(3.4)', 'E492:') call assert_equal("\nx", execute("echo \"x\"", 3.4)) - call CheckDefExecAndScriptFailure(['execute("echo \"x\"", 3.4)'], 'E806:') + call CheckDefExecAndScriptFailure2(['execute("echo \"x\"", 3.4)'], 'E1013: Argument 2: type mismatch, expected string but got float', 'E806:') endif endfunc diff --git a/src/testdir/test_fileformat.vim b/src/testdir/test_fileformat.vim index 07819dc2e6..6ba6e49ada 100644 --- a/src/testdir/test_fileformat.vim +++ b/src/testdir/test_fileformat.vim @@ -1,5 +1,7 @@ " Test for 'fileformat' +source shared.vim + " Test behavior of fileformat after bwipeout of last buffer func Test_fileformat_after_bw() bwipeout @@ -308,4 +310,18 @@ func Test_fileformat_plusplus_read() call assert_fails('e ++abc1 Xfile1', 'E474:') endfunc +" When Vim starts up with an empty buffer the first item in 'fileformats' is +" used as the 'fileformat'. +func Test_fileformat_on_startup() + let after =<< trim END + call writefile([&fileformat], 'Xfile', 'a') + quit + END + call RunVim(["set ffs=dos,unix,mac"], after, '') + call RunVim(["set ffs=mac,dos,unix"], after, '') + call RunVim(["set ffs=unix,mac,dos"], after, '') + call assert_equal(['dos', 'mac', 'unix'], readfile('Xfile')) + call delete('Xfile') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_global.vim b/src/testdir/test_global.vim index 4a01be4194..48753cfcc8 100644 --- a/src/testdir/test_global.vim +++ b/src/testdir/test_global.vim @@ -83,4 +83,8 @@ func Test_global_newline() close! endfunc +func Test_wrong_delimiter() + call assert_fails('g x^bxd', 'E146:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim index 6c8203f652..9ba9a805b1 100644 --- a/src/testdir/test_gui.vim +++ b/src/testdir/test_gui.vim @@ -389,7 +389,7 @@ func Test_set_guifont() endif " This only works if 'renderoptions' exists and does not work for Windows XP - " and older. + " and older. if exists('+renderoptions') && windowsversion() !~ '^[345]\.' " doing this four times used to cause a crash set renderoptions=type:directx @@ -880,4 +880,287 @@ func Test_gui_recursive_mapping() nunmap a endfunc +" Test GUI mouse events +func Test_gui_mouse_event() + set mousemodel=extend + call test_override('no_query_mouse', 1) + new + call setline(1, ['one two three', 'four five six']) + + " place the cursor using left click in normal mode + call cursor(1, 1) + call test_gui_mouse_event(0, 2, 4, 0, 0) + call test_gui_mouse_event(3, 2, 4, 0, 0) + call feedkeys("\", 'Lx!') + call assert_equal([0, 2, 4, 0], getpos('.')) + + " select and yank a word + let @" = '' + call test_gui_mouse_event(0, 1, 9, 0, 0) + call test_gui_mouse_event(0, 1, 9, 1, 0) + call test_gui_mouse_event(3, 1, 9, 0, 0) + call feedkeys("y", 'Lx!') + call assert_equal('three', @") + + " create visual selection using right click + let @" = '' + call test_gui_mouse_event(0, 2, 6, 0, 0) + call test_gui_mouse_event(3, 2, 6, 0, 0) + call test_gui_mouse_event(2, 2, 13, 0, 0) + call test_gui_mouse_event(3, 2, 13, 0, 0) + call feedkeys("y", 'Lx!') + call assert_equal('five six', @") + + " paste using middle mouse button + let @* = 'abc ' + call feedkeys('""', 'Lx!') + call test_gui_mouse_event(1, 1, 9, 0, 0) + call test_gui_mouse_event(3, 1, 9, 0, 0) + call feedkeys("\", 'Lx!') + call assert_equal(['one two abc three', 'four five six'], getline(1, '$')) + + " extend visual selection using right click in visual mode + let @" = '' + call cursor(1, 1) + call feedkeys('v', 'Lx!') + call test_gui_mouse_event(2, 1, 17, 0, 0) + call test_gui_mouse_event(3, 1, 17, 0, 0) + call feedkeys("y", 'Lx!') + call assert_equal('one two abc three', @") + + " extend visual selection using mouse drag + let @" = '' + call cursor(1, 1) + call test_gui_mouse_event(0, 2, 1, 0, 0) + call test_gui_mouse_event(0x43, 2, 9, 0, 0) + call test_gui_mouse_event(0x3, 2, 9, 0, 0) + call feedkeys("y", 'Lx!') + call assert_equal('four five', @") + + " select text by moving the mouse + let @" = '' + call cursor(1, 1) + redraw! + call test_gui_mouse_event(0, 1, 4, 0, 0) + call test_gui_mouse_event(0x700, 1, 9, 0, 0) + call test_gui_mouse_event(0x700, 1, 13, 0, 0) + call test_gui_mouse_event(0x3, 1, 13, 0, 0) + call feedkeys("y", 'Lx!') + call assert_equal(' two abc t', @") + + " Using mouse in insert mode + call cursor(1, 1) + call feedkeys('i', 't') + call test_gui_mouse_event(0, 2, 11, 0, 0) + call test_gui_mouse_event(3, 2, 11, 0, 0) + call feedkeys("po\", 'Lx!') + call assert_equal(['one two abc three', 'four five posix'], getline(1, '$')) + + %d _ + call setline(1, range(1, 100)) + " scroll up + call test_gui_mouse_event(0x200, 2, 1, 0, 0) + call test_gui_mouse_event(0x200, 2, 1, 0, 0) + call test_gui_mouse_event(0x200, 2, 1, 0, 0) + call feedkeys("H", 'Lx!') + call assert_equal(10, line('.')) + + " scroll down + call test_gui_mouse_event(0x100, 2, 1, 0, 0) + call test_gui_mouse_event(0x100, 2, 1, 0, 0) + call feedkeys("H", 'Lx!') + call assert_equal(4, line('.')) + + %d _ + set nowrap + call setline(1, range(10)->join('')->repeat(10)) + " scroll left + call test_gui_mouse_event(0x500, 1, 5, 0, 0) + call test_gui_mouse_event(0x500, 1, 10, 0, 0) + call test_gui_mouse_event(0x500, 1, 15, 0, 0) + call feedkeys('g0', 'Lx!') + call assert_equal(19, col('.')) + + " scroll right + call test_gui_mouse_event(0x600, 1, 15, 0, 0) + call test_gui_mouse_event(0x600, 1, 10, 0, 0) + call feedkeys('g0', 'Lx!') + call assert_equal(7, col('.')) + set wrap& + + %d _ + call setline(1, repeat([repeat('a', 60)], 10)) + + " record various mouse events + let mouseEventNames = [ + \ 'LeftMouse', 'LeftRelease', '2-LeftMouse', '3-LeftMouse', + \ 'S-LeftMouse', 'A-LeftMouse', 'C-LeftMouse', 'MiddleMouse', + \ 'MiddleRelease', '2-MiddleMouse', '3-MiddleMouse', + \ 'S-MiddleMouse', 'A-MiddleMouse', 'C-MiddleMouse', + \ 'RightMouse', 'RightRelease', '2-RightMouse', + \ '3-RightMouse', 'S-RightMouse', 'A-RightMouse', 'C-RightMouse', + \ 'X1Mouse', 'S-X1Mouse', 'A-X1Mouse', 'C-X1Mouse', 'X2Mouse', + \ 'S-X2Mouse', 'A-X2Mouse', 'C-X2Mouse' + \ ] + let mouseEventCodes = map(copy(mouseEventNames), "'<' .. v:val .. '>'") + let g:events = [] + for e in mouseEventCodes + exe 'nnoremap ' .. e .. ' call add(g:events, "' .. + \ substitute(e, '[<>]', '', 'g') .. '")' + endfor + + " Test various mouse buttons (0 - Left, 1 - Middle, 2 - Right, 0x300 - X1, + " 0x300- X2) + for button in [0, 1, 2, 0x300, 0x400] + " Single click + call test_gui_mouse_event(button, 2, 5, 0, 0) + call test_gui_mouse_event(3, 2, 5, 0, 0) + + " Double/Triple click is supported by only the Left/Middle/Right mouse + " buttons + if button <= 2 + " Double Click + call test_gui_mouse_event(button, 2, 5, 0, 0) + call test_gui_mouse_event(button, 2, 5, 1, 0) + call test_gui_mouse_event(3, 2, 5, 0, 0) + + " Triple Click + call test_gui_mouse_event(button, 2, 5, 0, 0) + call test_gui_mouse_event(button, 2, 5, 1, 0) + call test_gui_mouse_event(button, 2, 5, 1, 0) + call test_gui_mouse_event(3, 2, 5, 0, 0) + endif + + " Shift click + call test_gui_mouse_event(button, 3, 7, 0, 4) + call test_gui_mouse_event(3, 3, 7, 0, 4) + + " Alt click + call test_gui_mouse_event(button, 3, 7, 0, 8) + call test_gui_mouse_event(3, 3, 7, 0, 8) + + " Ctrl click + call test_gui_mouse_event(button, 3, 7, 0, 16) + call test_gui_mouse_event(3, 3, 7, 0, 16) + + call feedkeys("\", 'Lx!') + endfor + + call assert_equal(['LeftMouse', 'LeftRelease', 'LeftMouse', '2-LeftMouse', + \ 'LeftMouse', '2-LeftMouse', '3-LeftMouse', 'S-LeftMouse', + \ 'A-LeftMouse', 'C-LeftMouse', 'MiddleMouse', 'MiddleRelease', + \ 'MiddleMouse', '2-MiddleMouse', 'MiddleMouse', '2-MiddleMouse', + \ '3-MiddleMouse', 'S-MiddleMouse', 'A-MiddleMouse', 'C-MiddleMouse', + \ 'RightMouse', 'RightRelease', 'RightMouse', '2-RightMouse', + \ 'RightMouse', '2-RightMouse', '3-RightMouse', 'S-RightMouse', + \ 'A-RightMouse', 'C-RightMouse', 'X1Mouse', 'S-X1Mouse', 'A-X1Mouse', + \ 'C-X1Mouse', 'X2Mouse', 'S-X2Mouse', 'A-X2Mouse', 'C-X2Mouse'], + \ g:events) + + for e in mouseEventCodes + exe 'nunmap ' .. e + endfor + + " modeless selection + set mouse= + let save_guioptions = &guioptions + set guioptions+=A + %d _ + call setline(1, ['one two three', 'four five sixteen']) + call cursor(1, 1) + redraw! + " Double click should select the word and copy it to clipboard + let @* = '' + call test_gui_mouse_event(0, 2, 11, 0, 0) + call test_gui_mouse_event(0, 2, 11, 1, 0) + call test_gui_mouse_event(3, 2, 11, 0, 0) + call feedkeys("\", 'Lx!') + call assert_equal([0, 1, 1, 0], getpos('.')) + call assert_equal('sixteen', @*) + " Right click should extend the selection from cursor + call cursor(1, 6) + redraw! + let @* = '' + call test_gui_mouse_event(2, 1, 11, 0, 0) + call test_gui_mouse_event(3, 1, 11, 0, 0) + call feedkeys("\", 'Lx!') + call assert_equal([0, 1, 6, 0], getpos('.')) + call assert_equal('wo thr', @*) + " Middle click should paste the clipboard contents + call cursor(2, 1) + redraw! + call test_gui_mouse_event(1, 1, 11, 0, 0) + call test_gui_mouse_event(3, 1, 11, 0, 0) + call feedkeys("\", 'Lx!') + call assert_equal([0, 2, 7, 0], getpos('.')) + call assert_equal('wo thrfour five sixteen', getline(2)) + set mouse& + let &guioptions = save_guioptions + + " Test invalid parameters for test_gui_mouse_event() + call assert_fails('call test_gui_mouse_event("", 1, 2, 3, 4)', 'E474:') + call assert_fails('call test_gui_mouse_event(0, "", 2, 3, 4)', 'E474:') + call assert_fails('call test_gui_mouse_event(0, 1, "", 3, 4)', 'E474:') + call assert_fails('call test_gui_mouse_event(0, 1, 2, "", 4)', 'E474:') + call assert_fails('call test_gui_mouse_event(0, 1, 2, 3, "")', 'E474:') + + bw! + call test_override('no_query_mouse', 0) + set mousemodel& +endfunc + +" Test for 'guitablabel' and 'guitabtooltip' options +func TestGuiTabLabel() + call add(g:TabLabels, v:lnum + 100) + let bufnrlist = tabpagebuflist(v:lnum) + return bufname(bufnrlist[tabpagewinnr(v:lnum) - 1]) +endfunc + +func TestGuiTabToolTip() + call add(g:TabToolTips, v:lnum + 200) + let bufnrlist = tabpagebuflist(v:lnum) + return bufname(bufnrlist[tabpagewinnr(v:lnum) - 1]) +endfunc + +func Test_gui_tablabel_tooltip() + %bw! + " Removing the tabline at the end of this test, reduces the window height by + " one. Save and restore it after the test. + let save_lines = &lines + edit one + set modified + tabnew two + set modified + tabnew three + set modified + let g:TabLabels = [] + set guitablabel=%{TestGuiTabLabel()} + call test_override('starting', 1) + redrawtabline + call test_override('starting', 0) + call assert_true(index(g:TabLabels, 101) != -1) + call assert_true(index(g:TabLabels, 102) != -1) + call assert_true(index(g:TabLabels, 103) != -1) + set guitablabel& + unlet g:TabLabels + + if has('gui_gtk') + " Only on GTK+, the tooltip function is called even if the mouse is not + " on the tabline. on Win32 and Motif, the tooltip function is called only + " when the mouse pointer is over the tabline. + let g:TabToolTips = [] + set guitabtooltip=%{TestGuiTabToolTip()} + call test_override('starting', 1) + redrawtabline + call test_override('starting', 0) + call assert_true(index(g:TabToolTips, 201) != -1) + call assert_true(index(g:TabToolTips, 202) != -1) + call assert_true(index(g:TabToolTips, 203) != -1) + set guitabtooltip& + unlet g:TabToolTips + endif + %bw! + let &lines = save_lines +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_let.vim b/src/testdir/test_let.vim index 1b8f74bcfe..c05a4cbc20 100644 --- a/src/testdir/test_let.vim +++ b/src/testdir/test_let.vim @@ -314,6 +314,7 @@ func Test_let_errors() let ch = test_null_channel() call assert_fails('let ch += 1', 'E734:') endif + call assert_fails('let name = "a" .. "b",', 'E488: Trailing characters: ,') " This test works only when the language is English if v:lang == "C" || v:lang =~ '^[Ee]n' diff --git a/src/testdir/test_number.vim b/src/testdir/test_number.vim index 93f8282e07..13f8de719d 100644 --- a/src/testdir/test_number.vim +++ b/src/testdir/test_number.vim @@ -298,4 +298,26 @@ func Test_relativenumber_colors() call delete('XTest_relnr') endfunc +" Test for displaying line numbers with 'rightleft' +func Test_number_rightleft() + CheckFeature rightleft + new + setlocal number + setlocal rightleft + call setline(1, range(1, 1000)) + normal! 9Gzt + redraw! + call assert_match('^\s\+9 9$', Screenline(1)) + normal! 10Gzt + redraw! + call assert_match('^\s\+01 10$', Screenline(1)) + normal! 100Gzt + redraw! + call assert_match('^\s\+001 100$', Screenline(1)) + normal! 1000Gzt + redraw! + call assert_match('^\s\+0001 1000$', Screenline(1)) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 147a3a4ae6..68d7db87ea 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -1552,8 +1552,8 @@ func Test_popup_filter() call assert_equal(9, getcurpos()[2]) call feedkeys('0', 'xt') call assert_equal('0', g:ignored) - redraw - call assert_equal(1, getcurpos()[2]) + normal! l + call assert_equal(2, getcurpos()[2]) " x closes the popup call feedkeys('x', 'xt') diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 18b774c8b9..a6a20a8451 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -134,6 +134,21 @@ func XlistTests(cchar) call assert_equal([' 2 Xtestfile1:1 col 3: Line1', \ ' 3: non-error 2', ' 4 Xtestfile2:2 col 2: Line2'], l) + " Ranged entries + call g:Xsetlist([{'lnum':10,'text':'Line1'}, + \ {'lnum':20,'col':10,'text':'Line2'}, + \ {'lnum':30,'col':15,'end_col':20,'text':'Line3'}, + \ {'lnum':40,'end_lnum':45,'text':'Line4'}, + \ {'lnum':50,'end_lnum':55,'col':15,'text':'Line5'}, + \ {'lnum':60,'end_lnum':65,'col':25,'end_col':35,'text':'Line6'}]) + let l = split(execute('Xlist', ""), "\n") + call assert_equal([' 1:10: Line1', + \ ' 2:20 col 10: Line2', + \ ' 3:30 col 15-20: Line3', + \ ' 4:40-45: Line4', + \ ' 5:50-55 col 15: Line5', + \ ' 6:60-65 col 25-35: Line6'], l) + " Different types of errors call g:Xsetlist([{'lnum':10,'col':5,'type':'W', 'text':'Warning','nr':11}, \ {'lnum':20,'col':10,'type':'e','text':'Error','nr':22}, @@ -644,6 +659,7 @@ func s:test_xhelpgrep(cchar) call assert_true(&buftype == 'help') call assert_true(winnr() == 1) call assert_true(winnr('$') == 2) + call assert_match('|\d\+ col \d\+-\d\+|', getbufline(winbufnr(2), 1)[0]) " This wipes out the buffer, make sure that doesn't cause trouble. Xclose @@ -1514,10 +1530,13 @@ func SetXlistTests(cchar, bnum) call s:setup_commands(a:cchar) call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 1}, - \ {'bufnr': a:bnum, 'lnum': 2}]) + \ {'bufnr': a:bnum, 'lnum': 2, 'end_lnum': 3, 'col': 4, 'end_col': 5}]) let l = g:Xgetlist() call assert_equal(2, len(l)) call assert_equal(2, l[1].lnum) + call assert_equal(3, l[1].end_lnum) + call assert_equal(4, l[1].col) + call assert_equal(5, l[1].end_col) Xnext call g:Xsetlist([{'bufnr': a:bnum, 'lnum': 3}], 'a') @@ -2852,7 +2871,9 @@ func XvimgrepTests(cchar) let l = g:Xgetlist() call assert_equal(2, len(l)) call assert_equal(8, l[0].col) + call assert_equal(11, l[0].end_col) call assert_equal(12, l[1].col) + call assert_equal(15, l[1].end_col) 1Xvimgrep ?Editor? Xtestfile* let l = g:Xgetlist() @@ -5098,15 +5119,21 @@ func Xtest_qftextfunc(cchar) call assert_equal('Tqfexpr', &quickfixtextfunc) call assert_equal('', \ g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc) - Xexpr ['F1:10:2:green', 'F1:20:4:blue'] + call g:Xsetlist([ + \ { 'filename': 'F1', 'lnum': 10, 'col': 2, + \ 'end_col': 7, 'text': 'green'}, + \ { 'filename': 'F1', 'lnum': 20, 'end_lnum': 25, 'col': 4, + \ 'end_col': 8, 'text': 'blue'}, + \ ]) + Xwindow call assert_equal('F1-L10C2-green', getline(1)) call assert_equal('F1-L20C4-blue', getline(2)) Xclose set quickfixtextfunc&vim Xwindow - call assert_equal('F1|10 col 2| green', getline(1)) - call assert_equal('F1|20 col 4| blue', getline(2)) + call assert_equal('F1|10 col 2-7| green', getline(1)) + call assert_equal('F1|20-25 col 4-8| blue', getline(2)) Xclose set efm& set quickfixtextfunc& @@ -5231,6 +5258,64 @@ func Test_qftextfunc() call Xtest_qftextfunc('l') endfunc +" Test for updating a location list for some other window and check that +" 'qftextfunc' uses the correct location list. +func Test_qftextfunc_other_loclist() + %bw! + call setloclist(0, [], 'f') + + " create a window and a location list for it and open the location list + " window + lexpr ['F1:10:12:one', 'F1:20:14:two'] + let w1_id = win_getid() + call setloclist(0, [], ' ', + \ {'lines': ['F1:10:12:one', 'F1:20:14:two'], + \ 'quickfixtextfunc': + \ {d -> map(getloclist(d.winid, {'id' : d.id, + \ 'items' : 1}).items[d.start_idx-1:d.end_idx-1], + \ "'Line ' .. v:val.lnum .. ', Col ' .. v:val.col")}}) + lwindow + let w2_id = win_getid() + + " create another window and a location list for it and open the location + " list window + topleft new + let w3_id = win_getid() + call setloclist(0, [], ' ', + \ {'lines': ['F2:30:32:eleven', 'F2:40:34:twelve'], + \ 'quickfixtextfunc': + \ {d -> map(getloclist(d.winid, {'id' : d.id, + \ 'items' : 1}).items[d.start_idx-1:d.end_idx-1], + \ "'Ligne ' .. v:val.lnum .. ', Colonne ' .. v:val.col")}}) + lwindow + let w4_id = win_getid() + + topleft new + lexpr ['F3:50:52:green', 'F3:60:54:blue'] + let w5_id = win_getid() + + " change the location list for some other window + call setloclist(0, [], 'r', {'lines': ['F3:55:56:aaa', 'F3:57:58:bbb']}) + call setloclist(w1_id, [], 'r', {'lines': ['F1:62:63:bbb', 'F1:64:65:ccc']}) + call setloclist(w3_id, [], 'r', {'lines': ['F2:76:77:ddd', 'F2:78:79:eee']}) + call assert_equal(['Line 62, Col 63', 'Line 64, Col 65'], + \ getbufline(winbufnr(w2_id), 1, '$')) + call assert_equal(['Ligne 76, Colonne 77', 'Ligne 78, Colonne 79'], + \ getbufline(winbufnr(w4_id), 1, '$')) + call setloclist(w2_id, [], 'r', {'lines': ['F1:32:33:fff', 'F1:34:35:ggg']}) + call setloclist(w4_id, [], 'r', {'lines': ['F2:46:47:hhh', 'F2:48:49:jjj']}) + call assert_equal(['Line 32, Col 33', 'Line 34, Col 35'], + \ getbufline(winbufnr(w2_id), 1, '$')) + call assert_equal(['Ligne 46, Colonne 47', 'Ligne 48, Colonne 49'], + \ getbufline(winbufnr(w4_id), 1, '$')) + + call win_gotoid(w5_id) + lwindow + call assert_equal(['F3|55 col 56| aaa', 'F3|57 col 58| bbb'], + \ getline(1, '$')) + %bw! +endfunc + " Running :lhelpgrep command more than once in a help window, doesn't jump to " the help topic func Test_lhelpgrep_from_help_window() @@ -5281,7 +5366,42 @@ func Test_add_invalid_entry_with_qf_window() call setqflist(['bb'], 'a') call assert_equal(1, line('$')) call assert_equal(['Xfile1|10| aa'], getline(1, '$')) - call assert_equal([{'lnum': 10, 'bufnr': bufnr('Xfile1'), 'col': 0, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'module': '', 'text': 'aa'}], getqflist()) + call assert_equal([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': 0 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 0 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10 col 666| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 0 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10 col 666| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': -456 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10 col 666-222| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': -123 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) + + call setqflist([{'lnum': 10 , 'end_lnum': 6 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , 'r') + call assert_equal(1 , line('$')) + call assert_equal(['Xfile1|10-6 col 666-222| aa'] , getline(1 , '$')) + call assert_equal([{'lnum': 10 , 'end_lnum': 6 , 'bufnr': bufnr('Xfile1') , 'col': 666 , 'end_col': 222 , 'pattern': '' , 'valid': 1 , 'vcol': 0 , 'nr': -1 , 'type': '' , 'module': '' , 'text': 'aa'}] , getqflist()) cclose endfunc diff --git a/src/testdir/test_smartindent.vim b/src/testdir/test_smartindent.vim index 47ded64d0f..7b58af2491 100644 --- a/src/testdir/test_smartindent.vim +++ b/src/testdir/test_smartindent.vim @@ -111,4 +111,27 @@ func Test_si_comment_line_continuation() close! endfunc +" When 'paste' is set, 'smartindent' should not take effect. +func Test_si_with_paste() + new + setlocal smartindent autoindent + set paste + " insert text that will trigger smartindent + exe "norm! i {\nif (x)\ni = 1;\n#define FOO 1\nj = 2;\n}" + exe "norm! Ok = 3;" + exe "norm! 4G>>" + call assert_equal([' {', 'if (x)', 'i = 1;', '#define FOO 1', + \ 'j = 2;', 'k = 3;', '}'], getline(1, '$')) + call assert_true(&smartindent) + set nopaste + %d _ + exe "norm! i {\nif (x)\ni = 1;\n#define FOO 1\nj = 2;\n}" + exe "norm! Ok = 3;" + exe "norm! 4G>>" + call assert_equal([' {', "\t if (x)", "\t\t i = 1;", + \ '#define FOO 1', "\t\t j = 2;", "\t k = 3;", ' }'], + \ getline(1, '$')) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim index d9730c1df3..5c69131fcd 100644 --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -1304,6 +1304,8 @@ func Test_write_in_vimrc() endfunc func Test_echo_true_in_cmd() + CheckNotGui + let lines =<< trim END echo v:true call writefile(['done'], 'Xresult') @@ -1315,7 +1317,6 @@ func Test_echo_true_in_cmd() endif call delete('Xscript') call delete('Xresult') - endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim index c789dc9615..92af0d0b0f 100644 --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -837,15 +837,16 @@ func Test_ltag() ltag third call assert_equal('Xfoo', bufname('')) call assert_equal(3, line('.')) - call assert_equal([{'lnum': 3, 'bufnr': bufnr('Xfoo'), 'col': 0, - \ 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': 0, 'type': '', - \ 'module': '', 'text': 'third'}], getloclist(0)) + call assert_equal([{'lnum': 3, 'end_lnum': 0, 'bufnr': bufnr('Xfoo'), + \ 'col': 0, 'end_col': 0, 'pattern': '', 'valid': 1, 'vcol': 0, + \ 'nr': 0, 'type': '', 'module': '', 'text': 'third'}], getloclist(0)) ltag second call assert_equal(2, line('.')) - call assert_equal([{'lnum': 0, 'bufnr': bufnr('Xfoo'), 'col': 0, - \ 'pattern': '^\Vint second() {}\$', 'valid': 1, 'vcol': 0, 'nr': 0, - \ 'type': '', 'module': '', 'text': 'second'}], getloclist(0)) + call assert_equal([{'lnum': 0, 'end_lnum': 0, 'bufnr': bufnr('Xfoo'), + \ 'col': 0, 'end_col': 0, 'pattern': '^\Vint second() {}\$', + \ 'valid': 1, 'vcol': 0, 'nr': 0, 'type': '', 'module': '', + \ 'text': 'second'}], getloclist(0)) call delete('Xtags') call delete('Xfoo') diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 7b7b62305b..1d3c20e9fe 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -283,6 +283,29 @@ def Test_assign_unpack() [v1, v2; _] = [1, 2, 3, 4, 5] assert_equal(1, v1) assert_equal(2, v2) + + var a = 1 + var b = 3 + [a, b] += [2, 4] + assert_equal(3, a) + assert_equal(7, b) + + [a, b] -= [1, 2] + assert_equal(2, a) + assert_equal(5, b) + + [a, b] *= [3, 2] + assert_equal(6, a) + assert_equal(10, b) + + [a, b] /= [2, 4] + assert_equal(3, a) + assert_equal(2, b) + + [a, b] = [17, 15] + [a, b] %= [5, 3] + assert_equal(2, a) + assert_equal(0, b) END CheckDefAndScriptSuccess(lines) @@ -1820,6 +1843,19 @@ def Test_assign_command_modifier() CheckDefAndScriptSuccess(lines) enddef +def Test_assign_alt_buf_register() + var lines =<< trim END + edit 'file_b1' + var b1 = bufnr() + edit 'file_b2' + var b2 = bufnr() + assert_equal(b1, bufnr('#')) + @# = b2 + assert_equal(b2, bufnr('#')) + END + CheckDefAndScriptSuccess(lines) +enddef + def Test_script_funcref_case() var lines =<< trim END var Len = (s: string): number => len(s) + 1 diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index b98c840ce0..c40780f4ae 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -141,6 +141,11 @@ def Test_add_blob() CheckScriptSuccess(lines) enddef +def Test_and() + CheckDefFailure(['echo and("x", 0x2)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo and(0x1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_append() new setline(1, range(3)) @@ -155,6 +160,22 @@ def Test_append() bwipe! enddef +def Test_argc() + CheckDefFailure(['echo argc("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_arglistid() + CheckDefFailure(['echo arglistid("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo arglistid(1, "y")'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['echo arglistid("x", "y")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_argv() + CheckDefFailure(['echo argv("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo argv(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['echo argv("x", "y")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_balloon_show() CheckGui CheckFeature balloon_eval @@ -256,6 +277,10 @@ def Test_chdir() assert_fails('chdir(true)', 'E1174') enddef +def Test_clearmatches() + CheckDefFailure(['echo clearmatches("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_col() new setline(1, 'asdf') @@ -311,6 +336,11 @@ def Test_cursor() CheckDefExecAndScriptFailure(lines, 'E475:') enddef +def Test_debugbreak() + CheckMSWindows + CheckDefFailure(['echo debugbreak("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_delete() var res: bool = delete('doesnotexist') assert_equal(true, res) @@ -324,6 +354,18 @@ def Test_executable() CheckDefExecFailure(['echo executable(true)'], 'E1174:') enddef +def Test_execute() + var res = execute("echo 'hello'") + assert_equal("\nhello", res) + res = execute(["echo 'here'", "echo 'there'"]) + assert_equal("\nhere\nthere", res) + + CheckDefFailure(['echo execute(123)'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['echo execute([123])'], 'E1013: Argument 1: type mismatch, expected list but got list') + CheckDefExecFailure(['echo execute(["xx", 123])'], 'E492') + CheckDefFailure(['echo execute("xx", 123)'], 'E1013: Argument 2: type mismatch, expected string but got number') +enddef + def Test_exepath() CheckDefExecFailure(['echo exepath(true)'], 'E1174:') CheckDefExecFailure(['echo exepath(v:null)'], 'E1174:') @@ -520,6 +562,64 @@ def Test_flattennew() CheckDefAndScriptFailure(lines, 'E1158:') enddef +" Test for float functions argument type +def Test_float_funcs_args() + CheckFeature float + + # acos() + CheckDefFailure(['echo acos("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # asin() + CheckDefFailure(['echo asin("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # atan() + CheckDefFailure(['echo atan("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # atan2() + CheckDefFailure(['echo atan2("a", 1.1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo atan2(1.2, "a")'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['echo atan2(1.2)'], 'E119:') + # ceil() + CheckDefFailure(['echo ceil("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # cos() + CheckDefFailure(['echo cos("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # cosh() + CheckDefFailure(['echo cosh("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # exp() + CheckDefFailure(['echo exp("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # float2nr() + CheckDefFailure(['echo float2nr("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # floor() + CheckDefFailure(['echo floor("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # fmod() + CheckDefFailure(['echo fmod(1.1, "a")'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['echo fmod("a", 1.1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo fmod(1.1)'], 'E119:') + # isinf() + CheckDefFailure(['echo isinf("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # isnan() + CheckDefFailure(['echo isnan("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # log() + CheckDefFailure(['echo log("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # log10() + CheckDefFailure(['echo log10("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # pow() + CheckDefFailure(['echo pow("a", 1.1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo pow(1.1, "a")'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['echo pow(1.1)'], 'E119:') + # round() + CheckDefFailure(['echo round("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # sin() + CheckDefFailure(['echo sin("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # sinh() + CheckDefFailure(['echo sinh("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # sqrt() + CheckDefFailure(['echo sqrt("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # tan() + CheckDefFailure(['echo tan("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # tanh() + CheckDefFailure(['echo tanh("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + # trunc() + CheckDefFailure(['echo trunc("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_fnamemodify() CheckDefSuccess(['echo fnamemodify(test_null_string(), ":p")']) CheckDefSuccess(['echo fnamemodify("", ":p")']) @@ -640,6 +740,20 @@ def Test_getcompletion() set wildignore& enddef +def Test_getcurpos() + CheckDefFailure(['echo getcursorcharpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_getcursorcharpos() + CheckDefFailure(['echo getcursorcharpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_getcwd() + CheckDefFailure(['echo getcwd("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo getcwd("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo getcwd(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_getloclist_return_type() var l = getloclist(1) l->assert_equal([]) @@ -680,6 +794,16 @@ def Test_getftype() CheckDefExecFailure(['echo getftype(v:null)'], 'E1174:') enddef +def Test_getjumplist() + CheckDefFailure(['echo getjumplist("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo getjumplist("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo getjumplist(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + +def Test_getmatches() + CheckDefFailure(['echo getmatches("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_getqflist_return_type() var l = getqflist() l->assert_equal([]) @@ -715,6 +839,22 @@ def Test_getregtype() assert_fails('getregtype("ab")', 'E1162:') enddef +def Test_gettabinfo() + CheckDefFailure(['echo gettabinfo("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_gettagstack() + CheckDefFailure(['echo gettagstack("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_getwininfo() + CheckDefFailure(['echo getwininfo("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_getwinpos() + CheckDefFailure(['echo getwinpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_glob() glob('runtest.vim', true, true, true)->assert_equal(['runtest.vim']) enddef @@ -727,6 +867,12 @@ def Test_has() has('eval', true)->assert_equal(1) enddef +def Test_haslocaldir() + CheckDefFailure(['echo haslocaldir("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo haslocaldir("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo haslocaldir(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_hasmapto() hasmapto('foobar', 'i', true)->assert_equal(0) iabbrev foo foobar @@ -778,6 +924,10 @@ def Test_insert() CheckDefFailure(['insert([2, 3], 1, "x")'], 'E1013: Argument 3: type mismatch, expected number but got string', 1) enddef +def Test_invert() + CheckDefFailure(['echo invert("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_keys_return_type() const var: list = {a: 1, b: 2}->keys() var->assert_equal(['a', 'b']) @@ -800,6 +950,10 @@ def SID(): number ->str2nr() enddef +def Test_listener_remove() + CheckDefFailure(['echo listener_remove("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_map_function_arg() var lines =<< trim END def MapOne(i: number, v: string): string @@ -902,6 +1056,16 @@ def Test_map_failure() delete('Xtmpfile') enddef +def Test_matcharg() + CheckDefFailure(['echo matcharg("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_matchdelete() + CheckDefFailure(['echo matchdelete("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo matchdelete("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo matchdelete(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_max() g:flag = true var l1: list = g:flag @@ -934,6 +1098,11 @@ def Test_nr2char() nr2char(97, true)->assert_equal('a') enddef +def Test_or() + CheckDefFailure(['echo or("x", 0x2)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo or(0x1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_readdir() eval expand('sautest')->readdir((e) => e[0] !=# '.') eval expand('sautest')->readdirex((e) => e.name[0] !=# '.') @@ -983,6 +1152,26 @@ def Test_reverse_return_type() res->assert_equal(6) enddef +def Test_screenattr() + CheckDefFailure(['echo screenattr("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo screenattr(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + +def Test_screenchar() + CheckDefFailure(['echo screenchar("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo screenchar(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + +def Test_screenchars() + CheckDefFailure(['echo screenchars("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo screenchars(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + +def Test_screenstring() + CheckDefFailure(['echo screenstring("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo screenstring(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef + def Test_search() new setline(1, ['foo', 'bar']) @@ -1145,6 +1334,10 @@ def Test_setbufvar() getbufvar('%', 'myvar')->assert_equal(123) enddef +def Test_setcmdpos() + CheckDefFailure(['echo setcmdpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_setloclist() var items = [{filename: '/tmp/file', lnum: 1, valid: true}] var what = {items: items} @@ -1160,6 +1353,10 @@ def Test_setreg() assert_fails('setreg("ab", 0)', 'E1162:') enddef +def Test_shiftwidth() + CheckDefFailure(['echo shiftwidth("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + def Test_slice() assert_equal('12345', slice('012345', 1)) assert_equal('123', slice('012345', 1, 4)) @@ -1297,6 +1494,20 @@ def Test_timer_paused() timer_stop(id) enddef +def Test_tolower() + CheckDefFailure(['echo tolower(1)'], 'E1013: Argument 1: type mismatch, expected string but got number') +enddef + +def Test_toupper() + CheckDefFailure(['echo toupper(1)'], 'E1013: Argument 1: type mismatch, expected string but got number') +enddef + +def Test_tr() + CheckDefFailure(['echo tr(1, "a", "b")'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['echo tr("a", 1, "b")'], 'E1013: Argument 2: type mismatch, expected string but got number') + CheckDefFailure(['echo tr("a", "a", 1)'], 'E1013: Argument 3: type mismatch, expected string but got number') +enddef + def Test_win_execute() assert_equal("\n" .. winnr(), win_execute(win_getid(), 'echo winnr()')) assert_equal('', win_execute(342343, 'echo winnr()')) @@ -1326,7 +1537,45 @@ def Test_winsaveview() CheckDefAndScriptFailure(lines, 'E1012: Type mismatch; expected list but got dict', 1) enddef +def Test_win_gettype() + CheckDefFailure(['echo win_gettype("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef +def Test_win_gotoid() + CheckDefFailure(['echo win_gotoid("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef +def Test_win_id2tabwin() + CheckDefFailure(['echo win_id2tabwin("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_win_id2win() + CheckDefFailure(['echo win_id2win("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_win_screenpos() + CheckDefFailure(['echo win_screenpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_winbufnr() + CheckDefFailure(['echo winbufnr("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_winheight() + CheckDefFailure(['echo winheight("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_winlayout() + CheckDefFailure(['echo winlayout("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_winwidth() + CheckDefFailure(['echo winwidth("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_xor() + CheckDefFailure(['echo xor("x", 0x2)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['echo xor(0x1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') +enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 9d2f697d8a..7938d91f85 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -452,6 +452,37 @@ def Test_disassemble_list_assign() res) enddef +def s:ListAssignWithOp() + var a = 2 + var b = 3 + [a, b] += [4, 5] +enddef + +def Test_disassemble_list_assign_with_op() + var res = execute('disass s:ListAssignWithOp') + assert_match('\d*_ListAssignWithOp\_s*' .. + 'var a = 2\_s*' .. + '\d STORE 2 in $0\_s*' .. + 'var b = 3\_s*' .. + '\d STORE 3 in $1\_s*' .. + '\[a, b\] += \[4, 5\]\_s*' .. + '\d\+ PUSHNR 4\_s*' .. + '\d\+ PUSHNR 5\_s*' .. + '\d\+ NEWLIST size 2\_s*' .. + '\d\+ CHECKLEN 2\_s*' .. + '\d\+ LOAD $0\_s*' .. + '\d\+ ITEM 0 with op\_s*' .. + '\d\+ OPNR +\_s*' .. + '\d\+ STORE $0\_s*' .. + '\d\+ LOAD $1\_s*' .. + '\d\+ ITEM 1 with op\_s*' .. + '\d\+ OPNR +\_s*' .. + '\d\+ STORE $1\_s*' .. + '\d\+ DROP\_s*' .. + '\d\+ RETURN void', + res) +enddef + def s:ListAdd() var l: list = [] add(l, 123) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index d7fb1df825..9170cc9563 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -2340,7 +2340,7 @@ def Test_expr7_dict() CheckScriptFailure(['vim9script', "var x = {xxx: 1,"], 'E723:', 2) CheckDefAndScriptFailure2(["var x = {['a']: xxx}"], 'E1001:', 'E121:', 1) CheckDefAndScriptFailure(["var x = {a: 1, a: 2}"], 'E721:', 1) - CheckDefExecAndScriptFailure2(["var x = g:anint.member"], 'E715:', 'E15:', 1) + CheckDefExecAndScriptFailure2(["var x = g:anint.member"], 'E715:', 'E488:', 1) CheckDefExecAndScriptFailure(["var x = g:dict_empty.member"], 'E716:', 1) CheckDefExecAndScriptFailure(['var x: dict = {a: 234, b: "1"}'], 'E1012:', 1) @@ -2943,7 +2943,9 @@ def Test_expr7_method_call() loclist->setloclist(0) assert_equal([{bufnr: bufnr, lnum: 42, + end_lnum: 0, col: 17, + end_col: 0, text: 'wrong', pattern: '', valid: 1, @@ -3052,7 +3054,7 @@ func Test_expr7_fails() call CheckDefAndScriptFailure2(["var x = [notfound]"], "E1001:", 'E121:', 1) - call CheckDefAndScriptFailure2(["var X = () => 123)"], "E488:", 'E15:', 1) + call CheckDefAndScriptFailure(["var X = () => 123)"], 'E488:', 1) call CheckDefAndScriptFailure(["var x = 123->((x) => x + 5)"], "E107:", 1) call CheckDefAndScriptFailure(["var x = ¬exist"], 'E113:', 1) @@ -3070,7 +3072,7 @@ func Test_expr7_fails() call CheckDefExecAndScriptFailure(["var x = +g:alist"], 'E745:', 1) call CheckDefExecAndScriptFailure(["var x = +g:adict"], 'E728:', 1) - call CheckDefAndScriptFailure2(["var x = ''", "var y = x.memb"], 'E715:', 'E15:', 2) + call CheckDefAndScriptFailure2(["var x = ''", "var y = x.memb"], 'E715:', 'E488:', 2) call CheckDefAndScriptFailure2(["'yes'->", "Echo()"], 'E488: Trailing characters: ->', 'E260: Missing name after ->', 1) @@ -3354,8 +3356,8 @@ func Test_expr7_trailing_fails() endfunc func Test_expr_fails() - call CheckDefAndScriptFailure2(["var x = '1'is2"], 'E488:', 'E15:', 1) - call CheckDefAndScriptFailure2(["var x = '1'isnot2"], 'E488:', 'E15:', 1) + call CheckDefAndScriptFailure(["var x = '1'is2"], 'E488:', 1) + call CheckDefAndScriptFailure(["var x = '1'isnot2"], 'E488:', 1) call CheckDefAndScriptFailure2(["CallMe ('yes')"], 'E476:', 'E492:', 1) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 925e7c82a1..301a55c875 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -90,6 +90,24 @@ def Test_compile_error_in_called_function() CheckScriptFailureList(lines, ['E1012:', 'E1191:']) enddef +def Test_wrong_function_name() + var lines =<< trim END + vim9script + func _Foo() + echo 'foo' + endfunc + END + CheckScriptFailure(lines, 'E128:') + + lines =<< trim END + vim9script + def _Foo() + echo 'foo' + enddef + END + CheckScriptFailure(lines, 'E128:') +enddef + def Test_autoload_name_mismatch() var dir = 'Xdir/autoload' mkdir(dir, 'p') @@ -987,6 +1005,20 @@ def Test_pass_legacy_lambda_to_def_func() Foo() END CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + def g:TestFunc(f: func()) + enddef + legacy call g:TestFunc({-> 0}) + delfunc g:TestFunc + + def g:TestFunc(f: func(number)) + enddef + legacy call g:TestFunc({nr -> 0}) + delfunc g:TestFunc + END + CheckScriptSuccess(lines) enddef " Default arg and varargs diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 9237fdfad8..1584f173a8 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -3212,7 +3212,7 @@ def Test_vim9_comment_not_compiled() 'if 1# comment3', ' echo "yes"', 'endif', - ], 'E15:') + ], 'E488:') CheckScriptFailure([ 'vim9script', @@ -3221,7 +3221,7 @@ def Test_vim9_comment_not_compiled() 'elseif 2#comment', ' echo "no"', 'endif', - ], 'E15:') + ], 'E488:') CheckScriptSuccess([ 'vim9script', @@ -3231,7 +3231,7 @@ def Test_vim9_comment_not_compiled() CheckScriptFailure([ 'vim9script', 'var v = 1# comment6', - ], 'E15:') + ], 'E488:') CheckScriptSuccess([ 'vim9script', @@ -3937,6 +3937,26 @@ def Test_mapping_line_number() delfunc g:FuncA enddef +def Test_option_modifier() + var lines =<< trim END + set hlsearch & hlsearch ! + call assert_equal(1, &hlsearch) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + set hlsearch & + END + CheckScriptFailure(lines, 'E518:') + + lines =<< trim END + vim9script + set hlsearch & hlsearch ! + END + CheckScriptFailure(lines, 'E518:') +enddef + " Keep this last, it messes up highlighting. def Test_substitute_cmd() new diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim index ef74e09dbd..6c1a711668 100644 --- a/src/testdir/test_viminfo.vim +++ b/src/testdir/test_viminfo.vim @@ -4,7 +4,7 @@ source check.vim source term_util.vim source shared.vim -function Test_viminfo_read_and_write() +func Test_viminfo_read_and_write() " First clear 'history', so that "hislen" is zero. Then set it again, " simulating Vim starting up. set history=0 @@ -12,6 +12,7 @@ function Test_viminfo_read_and_write() set history=1000 call histdel(':') + let @/='' let lines = [ \ '# comment line', \ '*encoding=utf-8', @@ -62,6 +63,7 @@ func Test_global_vars() let g:MY_GLOBAL_NULL = test_null let test_none = v:none let g:MY_GLOBAL_NONE = test_none + let g:MY_GLOBAL_FUNCREF = function('min') set viminfo='100,<50,s10,h,!,nviminfo wv! Xviminfo @@ -76,6 +78,7 @@ func Test_global_vars() unlet g:MY_GLOBAL_TRUE unlet g:MY_GLOBAL_NULL unlet g:MY_GLOBAL_NONE + unlet g:MY_GLOBAL_FUNCREF rv! Xviminfo call assert_equal("Vim Editor", g:MY_GLOBAL_STRING) @@ -88,6 +91,37 @@ func Test_global_vars() call assert_equal(test_true, g:MY_GLOBAL_TRUE) call assert_equal(test_null, g:MY_GLOBAL_NULL) call assert_equal(test_none, g:MY_GLOBAL_NONE) + call assert_false(exists("g:MY_GLOBAL_FUNCREF")) + + " When reading global variables from viminfo, if a variable cannot be + " modified, then the value should not be changed. + unlet g:MY_GLOBAL_STRING + unlet g:MY_GLOBAL_NUM + unlet g:MY_GLOBAL_FLOAT + unlet g:MY_GLOBAL_DICT + unlet g:MY_GLOBAL_LIST + unlet g:MY_GLOBAL_BLOB + + const g:MY_GLOBAL_STRING = 'New Value' + const g:MY_GLOBAL_NUM = 987 + const g:MY_GLOBAL_FLOAT = 1.16 + const g:MY_GLOBAL_DICT = {'editor': 'vim'} + const g:MY_GLOBAL_LIST = [5, 7, 13] + const g:MY_GLOBAL_BLOB = 0zDEADBEEF + call assert_fails('rv! Xviminfo', 'E741:') + call assert_equal('New Value', g:MY_GLOBAL_STRING) + call assert_equal(987, g:MY_GLOBAL_NUM) + call assert_equal(1.16, g:MY_GLOBAL_FLOAT) + call assert_equal({'editor': 'vim'}, g:MY_GLOBAL_DICT) + call assert_equal([5, 7 , 13], g:MY_GLOBAL_LIST) + call assert_equal(0zDEADBEEF, g:MY_GLOBAL_BLOB) + + unlet g:MY_GLOBAL_STRING + unlet g:MY_GLOBAL_NUM + unlet g:MY_GLOBAL_FLOAT + unlet g:MY_GLOBAL_DICT + unlet g:MY_GLOBAL_LIST + unlet g:MY_GLOBAL_BLOB " Test for invalid values for a blob, list, dict in a viminfo file call writefile([ @@ -97,7 +131,7 @@ func Test_global_vars() \ "!GLOB_BLOB_4\tBLO\t0z12 ab", \ "!GLOB_LIST_1\tLIS\t1 2", \ "!GLOB_DICT_1\tDIC\t1 2"], 'Xviminfo') - call assert_fails('rv! Xviminfo', 'E15:') + call assert_fails('rv! Xviminfo', 'E488:') call assert_equal('123', g:GLOB_BLOB_1) call assert_equal(1, type(g:GLOB_BLOB_1)) call assert_equal('012', g:GLOB_BLOB_2) @@ -199,7 +233,26 @@ func Test_cmdline_history() call assert_equal("echo " . long800, histget(':', -2)) call assert_equal("echo 'one'", histget(':', -3)) + " If the value for the '/' or ':' or '@' field in 'viminfo' is zero, then + " the corresponding history entries are not saved. + set viminfo='100,/0,:0,@0,<50,s10,h,!,nviminfo + call histdel('/') + call histdel(':') + call histdel('@') + call histadd('/', 'foo') + call histadd(':', 'bar') + call histadd('@', 'baz') + wviminfo! Xviminfo + call histdel('/') + call histdel(':') + call histdel('@') + rviminfo! Xviminfo + call assert_equal('', histget('/')) + call assert_equal('', histget(':')) + call assert_equal('', histget('@')) + call delete('Xviminfo') + set viminfo&vim endfunc func Test_cmdline_history_order() @@ -315,7 +368,33 @@ func Test_viminfo_registers() let len += 1 endwhile + " If the maximum number of lines saved for a register ('<' in 'viminfo') is + " zero, then register values should not be saved. + let @a = 'abc' + set viminfo='100,<0,s10,h,!,nviminfo + wviminfo Xviminfo + let @a = 'xyz' + rviminfo! Xviminfo + call assert_equal('xyz', @a) + " repeat the test with '"' instead of '<' + let @b = 'def' + set viminfo='100,\"0,s10,h,!,nviminfo + wviminfo Xviminfo + let @b = 'rst' + rviminfo! Xviminfo + call assert_equal('rst', @b) + + " If the maximum size of an item ('s' in 'viminfo') is zero, then register + " values should not be saved. + let @c = '123' + set viminfo='100,<20,s0,h,!,nviminfo + wviminfo Xviminfo + let @c = '456' + rviminfo! Xviminfo + call assert_equal('456', @c) + call delete('Xviminfo') + set viminfo&vim endfunc func Test_viminfo_marks() @@ -510,6 +589,35 @@ func Test_viminfo_bad_syntax() call delete('Xviminfo') endfunc +func Test_viminfo_bad_syntax2() + let lines = [] + call add(lines, '|1,4') + + " bad viminfo syntax for history barline + call add(lines, '|2') " invalid number of fields in a history barline + call add(lines, '|2,9,1,1,"x"') " invalid value for the history type + call add(lines, '|2,0,,1,"x"') " no timestamp + call add(lines, '|2,0,1,1,10') " non-string text + + " bad viminfo syntax for register barline + call add(lines, '|3') " invalid number of fields in a register barline + call add(lines, '|3,1,1,1,1,,1,"x"') " missing width field + call add(lines, '|3,0,80,1,1,1,1,"x"') " invalid register number + call add(lines, '|3,0,10,5,1,1,1,"x"') " invalid register type + call add(lines, '|3,0,10,1,20,1,1,"x"') " invalid line count + call add(lines, '|3,0,10,1,0,1,1') " zero line count + + " bad viminfo syntax for mark barline + call add(lines, '|4') " invalid number of fields in a mark barline + call add(lines, '|4,1,1,1,1,1') " invalid value for file name + call add(lines, '|4,20,1,1,1,"x"') " invalid value for file name + call add(lines, '|4,49,0,1,1,"x"') " invalid value for line number + + call writefile(lines, 'Xviminfo') + rviminfo Xviminfo + call delete('Xviminfo') +endfunc + func Test_viminfo_file_marks() silent! bwipe test_viminfo.vim silent! bwipe Xviminfo @@ -664,6 +772,7 @@ func Test_viminfo_bufferlist() " If there are arguments, then :rviminfo doesn't read the buffer list. " Need to delete all the arguments for :rviminfo to work. %argdelete + set viminfo&vim edit Xfile1 edit Xfile2 @@ -684,9 +793,42 @@ func Test_viminfo_bufferlist() call assert_equal('Xfile1', bufname(l[1].bufnr)) call assert_equal('Xfile2', bufname(l[2].bufnr)) + " The quickfix, terminal, unlisted, unnamed buffers are not stored in the + " viminfo file + %bw! + edit Xfile1 + new + setlocal nobuflisted + new + copen + if has('terminal') + terminal + endif + wviminfo! Xviminfo + %bwipe! + rviminfo Xviminfo + let l = getbufinfo() + call assert_equal(2, len(l)) + call assert_true(bufexists('Xfile1')) + + " If a count is specified for '%', then only that many buffers should be + " stored in the viminfo file. + %bw! + set viminfo&vim + new Xbuf1 + new Xbuf2 + set viminfo+=%1 + wviminfo! Xviminfo + %bwipe! + rviminfo! Xviminfo + let l = getbufinfo() + call assert_equal(2, len(l)) + call assert_true(bufexists('Xbuf1')) + call assert_false(bufexists('Xbuf2')) + call delete('Xviminfo') %bwipe - set viminfo-=% + set viminfo&vim endfunc " Test for errors in a viminfo file @@ -747,6 +889,12 @@ func Test_viminfo_registers_old() \ ' Vim', \ '"a CHAR 0', \ ' red', + \ '"c BLOCK 0', + \ ' a', + \ ' d', + \ '"d LINE 0', + \ ' abc', + \ ' def', \ '"m@ CHAR 0', \ " :echo 'Hello'\", \ "", @@ -760,7 +908,12 @@ func Test_viminfo_registers_old() silent! normal @t rviminfo! Xviminfo call assert_equal('red', getreg('a')) + call assert_equal("v", getregtype('a')) call assert_equal('two', getreg('b')) + call assert_equal("a\nd", getreg('c')) + call assert_equal("\1", getregtype('c')) + call assert_equal("abc\ndef\n", getreg('d')) + call assert_equal("V", getregtype('d')) call assert_equal(":echo 'Hello'\", getreg('m')) call assert_equal('Vim', getreg('"')) call assert_equal("\nHello", execute('normal @@')) @@ -811,6 +964,7 @@ func Test_viminfo_perm() " Try to write the viminfo to a directory call mkdir('Xdir') call assert_fails('wviminfo Xdir', 'E137:') + call assert_fails('rviminfo Xdir', 'E195:') call delete('Xdir', 'rf') endfunc @@ -914,4 +1068,228 @@ func Test_viminfo_oldfiles_newfile() let &viminfofile = save_viminfofile endfunc +" When writing CTRL-V or "\n" to a viminfo file, it is converted to CTRL-V +" CTRL-V and CTRL-V n respectively. +func Test_viminfo_with_Ctrl_V() + silent! exe "normal! /\\\n" + wviminfo Xviminfo + call assert_notequal(-1, readfile('Xviminfo')->index("?/\\")) + let @/ = 'abc' + rviminfo! Xviminfo + call assert_equal("\", @/) + silent! exe "normal! /\\\n" + wviminfo Xviminfo + call assert_notequal(-1, readfile('Xviminfo')->index("?/\n")) + let @/ = 'abc' + rviminfo! Xviminfo + call assert_equal("\n", @/) + call delete('Xviminfo') +endfunc + +" Test for the 'r' field in 'viminfo' (removal media) +func Test_viminfo_removable_media() + CheckUnix + if !isdirectory('/tmp') || getftype('/tmp') != 'dir' + return + endif + let save_viminfo = &viminfo + set viminfo+=r/tmp + edit /tmp/Xvima1b2c3 + wviminfo Xviminfo + let matches = readfile('Xviminfo')->filter("v:val =~ 'Xvima1b2c3'") + call assert_equal(0, matches->len()) + let &viminfo = save_viminfo + call delete('Xviminfo') +endfunc + +" Test for the 'h' flag in 'viminfo'. If 'h' is not present, then the last +" search pattern read from 'viminfo' should be highlighted with 'hlsearch'. +" If 'h' is present, then the last search pattern should not be highlighted. +func Test_viminfo_hlsearch() + set viminfo&vim + + new + call setline(1, ['one two three']) + " save the screen attribute for the Search highlighted text and the normal + " text for later comparison + set hlsearch + let @/ = 'three' + redraw! + let hiSearch = screenattr(1, 9) + let hiNormal = screenattr(1, 1) + + set viminfo-=h + let @/='two' + wviminfo! Xviminfo + let @/='one' + rviminfo! Xviminfo + redraw! + call assert_equal(hiSearch, screenattr(1, 5)) + call assert_equal(hiSearch, screenattr(1, 6)) + call assert_equal(hiSearch, screenattr(1, 7)) + + set viminfo+=h + let @/='two' + wviminfo! Xviminfo + let @/='one' + rviminfo! Xviminfo + redraw! + call assert_equal(hiNormal, screenattr(1, 5)) + call assert_equal(hiNormal, screenattr(1, 6)) + call assert_equal(hiNormal, screenattr(1, 7)) + + call delete('Xviminfo') + set hlsearch& viminfo&vim + bw! +endfunc + +" Test for restoring the magicness of the last search pattern from the viminfo +" file. +func Test_viminfo_last_spat_magic() + set viminfo&vim + new + call setline(1, ' one abc a.c') + + " restore 'nomagic' + set nomagic + exe "normal gg/a.c\" + wviminfo! Xviminfo + set magic + exe "normal gg/one\" + rviminfo! Xviminfo + exe "normal! gg/\" + call assert_equal(10, col('.')) + + " restore 'magic' + set magic + exe "normal gg/a.c\" + wviminfo! Xviminfo + set nomagic + exe "normal gg/one\" + rviminfo! Xviminfo + exe "normal! gg/\" + call assert_equal(6, col('.')) + + call delete('Xviminfo') + set viminfo&vim magic& + bw! +endfunc + +" Test for restoring the smartcase of the last search pattern from the viminfo +" file. +func Test_viminfo_last_spat_smartcase() + new + call setline(1, ' one abc Abc') + set ignorecase smartcase + + " Searching with * should disable smartcase + exe "normal! gg$b*" + wviminfo! Xviminfo + exe "normal gg/one\" + rviminfo! Xviminfo + exe "normal! gg/\" + call assert_equal(6, col('.')) + + call delete('Xviminfo') + set ignorecase& smartcase& viminfo& + bw! +endfunc + +" Test for restoring the last search pattern with a line or character offset +" from the viminfo file. +func Test_viminfo_last_spat_offset() + new + call setline(1, ['one', 'two', 'three', 'four', 'five']) + " line offset + exe "normal! /two/+2\" + wviminfo! Xviminfo + exe "normal gg/five\" + rviminfo! Xviminfo + exe "normal! gg/\" + call assert_equal(4, line('.')) + " character offset + exe "normal! gg/^th/e+2\" + wviminfo! Xviminfo + exe "normal gg/two\" + rviminfo! Xviminfo + exe "normal! gg/\" + call assert_equal([3, 4], [line('.'), col('.')]) + call delete('Xviminfo') + bw! +endfunc + +" Test for saving and restoring the last executed register (@ command) +" from the viminfo file +func Test_viminfo_last_exec_reg() + let g:val = 1 + let @a = ":let g:val += 1\n" + normal! @a + wviminfo! Xviminfo + let @b = '' + normal! @b + rviminfo! Xviminfo + normal @@ + call assert_equal(3, g:val) + call delete('Xviminfo') +endfunc + +" Test for merging file marks in a viminfo file +func Test_viminfo_merge_file_marks() + for [f, l, t] in [['a.txt', 5, 10], ['b.txt', 10, 20]] + call test_settime(t) + exe 'edit ' .. f + call setline(1, range(1, 20)) + exe l . 'mark a' + wviminfo Xviminfo + bw! + endfor + call test_settime(30) + for [f, l] in [['a.txt', 5], ['b.txt', 10]] + exe 'edit ' .. f + rviminfo! Xviminfo + call assert_equal(l, line("'a")) + bw! + endfor + call delete('Xviminfo') + call test_settime(0) +endfunc + +" Test for merging file marks from a old viminfo file +func Test_viminfo_merge_old_filemarks() + let lines = [] + call add(lines, '|1,4') + call add(lines, '> ' .. fnamemodify('a.txt', ':p:~')) + call add(lines, "\tb\t7\t0\n") + call writefile(lines, 'Xviminfo') + edit b.txt + call setline(1, range(1, 20)) + 12mark b + wviminfo Xviminfo + bw! + edit a.txt + rviminfo! Xviminfo + call assert_equal(7, line("'b")) + edit b.txt + rviminfo! Xviminfo + call assert_equal(12, line("'b")) + call delete('Xviminfo') +endfunc + +" Test for merging the jump list from a old viminfo file +func Test_viminfo_merge_old_jumplist() + let lines = [] + call add(lines, "-' 10 1 " .. fnamemodify('a.txt', ':p:~')) + call add(lines, "-' 20 1 " .. fnamemodify('a.txt', ':p:~')) + call add(lines, "-' 30 1 " .. fnamemodify('b.txt', ':p:~')) + call add(lines, "-' 40 1 " .. fnamemodify('b.txt', ':p:~')) + call writefile(lines, 'Xviminfo') + clearjumps + rviminfo! Xviminfo + let l = getjumplist()[0] + call assert_equal([40, 30, 20, 10], [l[0].lnum, l[1].lnum, l[2].lnum, + \ l[3].lnum]) + bw! + call delete('Xviminfo') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim index 89c100189b..de876589af 100644 --- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -5570,7 +5570,7 @@ func Test_expr_eval_error_msg() call T(19, '{(1} + CONT(19)', 'E110', "Missing ')'") call T(20, '("abc"[1) + CONT(20)', 'E111', "Missing ']'") call T(21, '(1 +) + CONT(21)', 'E15', "Invalid expression") - call T(22, '1 2 + CONT(22)', 'E15', "Invalid expression") + call T(22, '1 2 + CONT(22)', 'E488', "Trailing characters: 2 +") call T(23, '(1 ? 2) + CONT(23)', 'E109', "Missing ':' after '?'") call T(24, '("abc) + CONT(24)', 'E114', "Missing quote") call T(25, "('abc) + CONT(25)", 'E115', "Missing quote") diff --git a/src/testing.c b/src/testing.c index cded3c73a0..5ebcefbdf0 100644 --- a/src/testing.c +++ b/src/testing.c @@ -1211,10 +1211,46 @@ f_test_scrollbar(typval_T *argvars, typval_T *rettv UNUSED) void f_test_setmouse(typval_T *argvars, typval_T *rettv UNUSED) { + if (argvars[0].v_type != VAR_NUMBER || (argvars[1].v_type) != VAR_NUMBER) + { + emsg(_(e_invarg)); + return; + } + mouse_row = (time_t)tv_get_number(&argvars[0]) - 1; mouse_col = (time_t)tv_get_number(&argvars[1]) - 1; } + void +f_test_gui_mouse_event(typval_T *argvars UNUSED, typval_T *rettv UNUSED) +{ +#ifdef FEAT_GUI + int button; + int row; + int col; + int repeated_click; + int_u mods; + + if (argvars[0].v_type != VAR_NUMBER + || (argvars[1].v_type) != VAR_NUMBER + || (argvars[2].v_type) != VAR_NUMBER + || (argvars[3].v_type) != VAR_NUMBER + || (argvars[4].v_type) != VAR_NUMBER) + { + emsg(_(e_invarg)); + return; + } + + button = tv_get_number(&argvars[0]); + row = tv_get_number(&argvars[1]); + col = tv_get_number(&argvars[2]); + repeated_click = tv_get_number(&argvars[3]); + mods = tv_get_number(&argvars[4]); + + gui_send_mouse_event(button, TEXT_X(col - 1), TEXT_Y(row - 1), repeated_click, mods); +#endif +} + void f_test_settime(typval_T *argvars, typval_T *rettv UNUSED) { diff --git a/src/undo.c b/src/undo.c index 67cbe3ab00..a8cfd20505 100644 --- a/src/undo.c +++ b/src/undo.c @@ -963,7 +963,9 @@ undo_flush(bufinfo_T *bi) { if (bi->bi_buffer != NULL && bi->bi_state != NULL && bi->bi_used > 0) { - crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used); + // Last parameter is only used for sodium encryption and that + // explicitly disables encryption of undofiles. + crypt_encode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_used, FALSE); if (fwrite(bi->bi_buffer, bi->bi_used, (size_t)1, bi->bi_fp) != 1) return FAIL; bi->bi_used = 0; @@ -995,7 +997,9 @@ fwrite_crypt(bufinfo_T *bi, char_u *ptr, size_t len) if (copy == NULL) return 0; } - crypt_encode(bi->bi_state, ptr, len, copy); + // Last parameter is only used for sodium encryption and that + // explicitly disables encryption of undofiles. + crypt_encode(bi->bi_state, ptr, len, copy, TRUE); i = fwrite(copy, len, (size_t)1, bi->bi_fp); if (copy != small_buf) vim_free(copy); @@ -1129,7 +1133,7 @@ undo_read(bufinfo_T *bi, char_u *buffer, size_t size) } bi->bi_avail = n; bi->bi_used = 0; - crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail); + crypt_decode_inplace(bi->bi_state, bi->bi_buffer, bi->bi_avail, FALSE); } n = size_todo; if (n > bi->bi_avail - bi->bi_used) @@ -1176,7 +1180,7 @@ read_string_decrypt(bufinfo_T *bi, int len) ptr[len] = NUL; #ifdef FEAT_CRYPT if (bi->bi_state != NULL && bi->bi_buffer == NULL) - crypt_decode_inplace(bi->bi_state, ptr, len); + crypt_decode_inplace(bi->bi_state, ptr, len, FALSE); #endif } return ptr; diff --git a/src/userfunc.c b/src/userfunc.c index 56b7df32f5..e2e745c338 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3595,7 +3595,8 @@ trans_function_name( lead += (int)STRLEN(sid_buf); } } - else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, len)) + else if (!(flags & TFN_INT) && (builtin_function(lv.ll_name, len) + || (in_vim9script() && *lv.ll_name == '_'))) { semsg(_("E128: Function name must start with a capital or \"s:\": %s"), start); diff --git a/src/version.c b/src/version.c index 6d8c923ba3..88ea03293e 100644 --- a/src/version.c +++ b/src/version.c @@ -563,6 +563,11 @@ static char *(features[]) = #else "-smartindent", #endif +#ifdef FEAT_SODIUM + "+sodium", +#else + "-sodium", +#endif #ifdef FEAT_SOUND "+sound", #else @@ -765,6 +770,62 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3036, +/**/ + 3035, +/**/ + 3034, +/**/ + 3033, +/**/ + 3032, +/**/ + 3031, +/**/ + 3030, +/**/ + 3029, +/**/ + 3028, +/**/ + 3027, +/**/ + 3026, +/**/ + 3025, +/**/ + 3024, +/**/ + 3023, +/**/ + 3022, +/**/ + 3021, +/**/ + 3020, +/**/ + 3019, +/**/ + 3018, +/**/ + 3017, +/**/ + 3016, +/**/ + 3015, +/**/ + 3014, +/**/ + 3013, +/**/ + 3012, +/**/ + 3011, +/**/ + 3010, +/**/ + 3009, /**/ 3008, /**/ diff --git a/src/vim.h b/src/vim.h index ea07b8a23e..128ddee84e 100644 --- a/src/vim.h +++ b/src/vim.h @@ -1813,9 +1813,9 @@ typedef enum { // Keep in sync with INSTRUCTIONS(). #ifdef FEAT_PROFILE -# define COMPILE_TYPE(ufunc) (debug_break_level > 0 ? CT_DEBUG : do_profiling == PROF_YES && (ufunc)->uf_profiling ? CT_PROFILE : CT_NONE) +# define COMPILE_TYPE(ufunc) (debug_break_level > 0 || ufunc->uf_has_breakpoint ? CT_DEBUG : do_profiling == PROF_YES && (ufunc)->uf_profiling ? CT_PROFILE : CT_NONE) #else -# define COMPILE_TYPE(ufunc) debug_break_level > 0 ? CT_DEBUG : CT_NONE +# define COMPILE_TYPE(ufunc) debug_break_level > 0 || ufunc->uf_has_breakpoint ? CT_DEBUG : CT_NONE #endif /* diff --git a/src/vim9.h b/src/vim9.h index b8c7133586..58d451cd11 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -209,6 +209,12 @@ typedef struct { int cuf_argcount; // number of arguments on top of stack } cufunc_T; +// arguments to ISN_GETITEM +typedef struct { + varnumber_T gi_index; + int gi_with_op; +} getitem_T; + typedef enum { JUMP_ALWAYS, JUMP_IF_FALSE, // pop and jump if false @@ -432,6 +438,7 @@ struct isn_S { isn_T *instr; tostring_T tostring; tobool_T tobool; + getitem_T getitem; } isn_arg; }; @@ -498,7 +505,7 @@ extern garray_T def_functions; // Keep in sync with COMPILE_TYPE() #ifdef FEAT_PROFILE # define INSTRUCTIONS(dfunc) \ - (debug_break_level > 0 \ + (debug_break_level > 0 || dfunc->df_ufunc->uf_has_breakpoint \ ? (dfunc)->df_instr_debug \ : ((do_profiling == PROF_YES && (dfunc->df_ufunc)->uf_profiling) \ ? (dfunc)->df_instr_prof \ diff --git a/src/vim9compile.c b/src/vim9compile.c index 663a52dc7b..c5a2c2dcf2 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1240,13 +1240,16 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type) /* * Generate an ISN_GETITEM instruction with "index". + * "with_op" is TRUE for "+=" and other operators, the stack has the current + * value below the list with values. */ static int -generate_GETITEM(cctx_T *cctx, int index) +generate_GETITEM(cctx_T *cctx, int index, int with_op) { isn_T *isn; garray_T *stack = &cctx->ctx_type_stack; - type_T *type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; + type_T *type = ((type_T **)stack->ga_data)[stack->ga_len + - (with_op ? 2 : 1)]; type_T *item_type = &t_any; RETURN_OK_IF_SKIP(cctx); @@ -1260,7 +1263,8 @@ generate_GETITEM(cctx_T *cctx, int index) item_type = type->tt_member; if ((isn = generate_instr(cctx, ISN_GETITEM)) == NULL) return FAIL; - isn->isn_arg.number = index; + isn->isn_arg.getitem.gi_index = index; + isn->isn_arg.getitem.gi_with_op = with_op; // add the item type to the type stack if (ga_grow(stack, 1) == FAIL) @@ -5852,7 +5856,7 @@ get_var_dest( return FAIL; } *dest = dest_reg; - *type = &t_string; + *type = name[1] == '#' ? &t_number_or_string : &t_string; } else if (STRNCMP(name, "g:", 2) == 0) { @@ -5927,7 +5931,8 @@ generate_store_var( case dest_env: return generate_STORE(cctx, ISN_STOREENV, 0, name + 1); case dest_reg: - return generate_STORE(cctx, ISN_STOREREG, name[1], NULL); + return generate_STORE(cctx, ISN_STOREREG, + name[1] == '@' ? '"' : name[1], NULL); case dest_vimvar: return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL); case dest_script: @@ -6745,19 +6750,17 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) int is_const = FALSE; char_u *wp; + // for "+=", "*=", "..=" etc. first load the current value + if (*op != '=' + && compile_load_lhs_with_index(&lhs, var_start, + cctx) == FAIL) + goto theend; + // For "var = expr" evaluate the expression. if (var_count == 0) { int r; - // for "+=", "*=", "..=" etc. first load the current value - if (*op != '=') - { - if (compile_load_lhs_with_index(&lhs, var_start, - cctx) == FAIL) - goto theend; - } - // Compile the expression. instr_count = instr->ga_len; if (incdec) @@ -6794,7 +6797,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) { // For "[var, var] = expr" get the "var_idx" item from the // list. - if (generate_GETITEM(cctx, var_idx) == FAIL) + if (generate_GETITEM(cctx, var_idx, *op != '=') == FAIL) goto theend; } @@ -6843,9 +6846,19 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) goto theend; } } - else if (*p != '=' && need_type(rhs_type, lhs.lhs_member_type, + else + { + type_T *lhs_type = lhs.lhs_member_type; + + // Special case: assigning to @# can use a number or a + // string. + if (lhs_type == &t_number_or_string + && rhs_type->tt_type == VAR_NUMBER) + lhs_type = &t_number; + if (*p != '=' && need_type(rhs_type, lhs_type, -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; + } } else if (cmdidx == CMD_final) { @@ -7711,6 +7724,7 @@ compile_for(char_u *arg_start, cctx_T *cctx) int semicolon = FALSE; size_t varlen; garray_T *stack = &cctx->ctx_type_stack; + garray_T *instr = &cctx->ctx_instr; scope_T *scope; lvar_T *loop_lvar; // loop iteration variable lvar_T *var_lvar; // variable for "var" @@ -7737,6 +7751,13 @@ compile_for(char_u *arg_start, cctx_T *cctx) if (may_get_next_line_error(wp, &p, cctx) == FAIL) return NULL; + // Remove the already generated ISN_DEBUG, it is written below the ISN_FOR + // instruction. + if (cctx->ctx_compile_type == CT_DEBUG && instr->ga_len > 0 + && ((isn_T *)instr->ga_data)[instr->ga_len - 1] + .isn_type == ISN_DEBUG) + --instr->ga_len; + scope = new_scope(cctx, FOR_SCOPE); if (scope == NULL) return NULL; @@ -7788,11 +7809,12 @@ compile_for(char_u *arg_start, cctx_T *cctx) item_type = vartype->tt_member->tt_member; } - // CMDMOD_REV must come before the FOR instruction + // CMDMOD_REV must come before the FOR instruction. generate_undo_cmdmods(cctx); // "for_end" is set when ":endfor" is found scope->se_u.se_for.fs_top_label = current_instr_idx(cctx); + generate_FOR(cctx, loop_lvar->lv_idx); arg = arg_start; @@ -7893,6 +7915,10 @@ compile_for(char_u *arg_start, cctx_T *cctx) vim_free(name); } + if (cctx->ctx_compile_type == CT_DEBUG) + // Add ISN_DEBUG here, so that the loop variables can be inspected. + generate_instr_debug(cctx); + return arg_end; failed: @@ -7927,7 +7953,7 @@ compile_endfor(char_u *arg, cctx_T *cctx) // At end of ":for" scope jump back to the FOR instruction. generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label); - // Fill in the "end" label in the FOR statement so it can jump here + // Fill in the "end" label in the FOR statement so it can jump here. isn = ((isn_T *)instr->ga_data) + forscope->fs_top_label; isn->isn_arg.forloop.for_end = instr->ga_len; @@ -8234,6 +8260,7 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED) #ifdef FEAT_PROFILE // the profile-start should be after the jump if (cctx->ctx_compile_type == CT_PROFILE + && instr->ga_len > 0 && ((isn_T *)instr->ga_data)[instr->ga_len - 1] .isn_type == ISN_PROF_START) --instr->ga_len; diff --git a/src/vim9execute.c b/src/vim9execute.c index e464732dda..6d0da61797 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -147,6 +147,23 @@ exe_newlist(int count, ectx_T *ectx) return OK; } +/* + * If debug_tick changed check if "ufunc" has a breakpoint and update + * "uf_has_breakpoint". + */ + static void +update_has_breakpoint(ufunc_T *ufunc) +{ + if (ufunc->uf_debug_tick != debug_tick) + { + linenr_T breakpoint; + + ufunc->uf_debug_tick = debug_tick; + breakpoint = dbg_find_breakpoint(FALSE, ufunc->uf_name, 0); + ufunc->uf_has_breakpoint = breakpoint > 0; + } +} + /* * Call compiled function "cdf_idx" from compiled code. * This adds a stack frame and sets the instruction pointer to the start of the @@ -212,6 +229,9 @@ call_dfunc( } #endif + // Update uf_has_breakpoint if needed. + update_has_breakpoint(ufunc); + // When debugging and using "cont" switches to the not-debugged // instructions, may need to still compile them. if ((func_needs_compiling(ufunc, COMPILE_TYPE(ufunc)) @@ -1394,7 +1414,7 @@ typedef struct subs_expr_S { // Set when calling do_debug(). static ectx_T *debug_context = NULL; -static int debug_arg_count; +static int debug_var_count; /* * When debugging lookup "name" and return the typeval. @@ -1405,23 +1425,100 @@ lookup_debug_var(char_u *name) { int idx; dfunc_T *dfunc; + ufunc_T *ufunc; ectx_T *ectx = debug_context; + int varargs_off; if (ectx == NULL) return NULL; dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx; // Go through the local variable names, from last to first. - for (idx = debug_arg_count - 1; idx >= 0; --idx) + for (idx = debug_var_count - 1; idx >= 0; --idx) { - char_u *s = ((char_u **)dfunc->df_var_names.ga_data)[idx]; - if (STRCMP(s, name) == 0) + if (STRCMP(((char_u **)dfunc->df_var_names.ga_data)[idx], name) == 0) return STACK_TV_VAR(idx); } + // Go through argument names. + ufunc = dfunc->df_ufunc; + varargs_off = ufunc->uf_va_name == NULL ? 0 : 1; + for (idx = 0; idx < ufunc->uf_args.ga_len; ++idx) + if (STRCMP(((char_u **)(ufunc->uf_args.ga_data))[idx], name) == 0) + return STACK_TV(ectx->ec_frame_idx - ufunc->uf_args.ga_len + - varargs_off + idx); + if (ufunc->uf_va_name != NULL && STRCMP(ufunc->uf_va_name, name) == 0) + return STACK_TV(ectx->ec_frame_idx - 1); + return NULL; } + static void +handle_debug(isn_T *iptr, ectx_T *ectx) +{ + char_u *line; + ufunc_T *ufunc = (((dfunc_T *)def_functions.ga_data) + + ectx->ec_dfunc_idx)->df_ufunc; + isn_T *ni; + int end_lnum = iptr->isn_lnum; + garray_T ga; + int lnum; + + if (ex_nesting_level > debug_break_level) + { + linenr_T breakpoint; + + if (!ufunc->uf_has_breakpoint) + return; + + // check for the next breakpoint if needed + breakpoint = dbg_find_breakpoint(FALSE, ufunc->uf_name, + iptr->isn_lnum - 1); + if (breakpoint <= 0 || breakpoint > iptr->isn_lnum) + return; + } + + SOURCING_LNUM = iptr->isn_lnum; + debug_context = ectx; + debug_var_count = iptr->isn_arg.number; + + for (ni = iptr + 1; ni->isn_type != ISN_FINISH; ++ni) + if (ni->isn_type == ISN_DEBUG + || ni->isn_type == ISN_RETURN + || ni->isn_type == ISN_RETURN_VOID) + { + end_lnum = ni->isn_lnum; + break; + } + + if (end_lnum > iptr->isn_lnum) + { + ga_init2(&ga, sizeof(char_u *), 10); + for (lnum = iptr->isn_lnum; lnum < end_lnum; ++lnum) + { + char_u *p = skipwhite( + ((char_u **)ufunc->uf_lines.ga_data)[lnum - 1]); + + if (*p == '#') + break; + if (ga_grow(&ga, 1) == OK) + ((char_u **)(ga.ga_data))[ga.ga_len++] = p; + if (STRNCMP(p, "def ", 4) == 0) + break; + } + line = ga_concat_strings(&ga, " "); + vim_free(ga.ga_data); + } + else + line = ((char_u **)ufunc->uf_lines.ga_data)[iptr->isn_lnum - 1]; + + do_debug(line == NULL ? (char_u *)"[empty]" : line); + debug_context = NULL; + + if (end_lnum > iptr->isn_lnum) + vim_free(line); +} + /* * Execute instructions in execution context "ectx". * Return OK or FAIL; @@ -2127,8 +2224,7 @@ exec_instructions(ectx_T *ectx) --ectx->ec_stack.ga_len; tv = STACK_TV_BOT(0); - write_reg_contents(reg == '@' ? '"' : reg, - tv_get_string(tv), -1, FALSE); + write_reg_contents(reg, tv_get_string(tv), -1, FALSE); clear_tv(tv); } break; @@ -3736,12 +3832,12 @@ exec_instructions(ectx_T *ectx) case ISN_GETITEM: { listitem_T *li; - int index = iptr->isn_arg.number; + getitem_T *gi = &iptr->isn_arg.getitem; // Get list item: list is at stack-1, push item. // List type and length is checked for when compiling. - tv = STACK_TV_BOT(-1); - li = list_find(tv->vval.v_list, index); + tv = STACK_TV_BOT(-1 - gi->gi_with_op); + li = list_find(tv->vval.v_list, gi->gi_index); if (GA_GROW(&ectx->ec_stack, 1) == FAIL) goto theend; @@ -3750,7 +3846,7 @@ exec_instructions(ectx_T *ectx) // Useful when used in unpack assignment. Reset at // ISN_DROP. - ectx->ec_where.wt_index = index + 1; + ectx->ec_where.wt_index = gi->gi_index + 1; ectx->ec_where.wt_variable = TRUE; } break; @@ -4144,22 +4240,7 @@ exec_instructions(ectx_T *ectx) break; case ISN_DEBUG: - if (ex_nesting_level <= debug_break_level) - { - char_u *line; - ufunc_T *ufunc = (((dfunc_T *)def_functions.ga_data) - + ectx->ec_dfunc_idx)->df_ufunc; - - SOURCING_LNUM = iptr->isn_lnum; - debug_context = ectx; - debug_arg_count = iptr->isn_arg.number; - line = ((char_u **)ufunc->uf_lines.ga_data)[ - iptr->isn_lnum - 1]; - if (line == NULL) - line = (char_u *)"[empty]"; - do_debug(line); - debug_context = NULL; - } + handle_debug(iptr, ectx); break; case ISN_SHUFFLE: @@ -4335,6 +4416,9 @@ call_def_function( #undef STACK_TV_VAR #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) + ectx.ec_frame_idx + STACK_FRAME_SIZE + idx) + // Update uf_has_breakpoint if needed. + update_has_breakpoint(ufunc); + if (ufunc->uf_def_status == UF_NOT_COMPILED || ufunc->uf_def_status == UF_COMPILE_ERROR || (func_needs_compiling(ufunc, COMPILE_TYPE(ufunc)) @@ -5292,8 +5376,10 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc) case ISN_ANYSLICE: smsg("%s%4d ANYSLICE", pfx, current); break; case ISN_SLICE: smsg("%s%4d SLICE %lld", pfx, current, iptr->isn_arg.number); break; - case ISN_GETITEM: smsg("%s%4d ITEM %lld", - pfx, current, iptr->isn_arg.number); break; + case ISN_GETITEM: smsg("%s%4d ITEM %lld%s", pfx, current, + iptr->isn_arg.getitem.gi_index, + iptr->isn_arg.getitem.gi_with_op ? + " with op" : ""); break; case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break; case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current, iptr->isn_arg.string); break; diff --git a/src/vim9type.c b/src/vim9type.c index f3718e4685..91dc3fe607 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -171,7 +171,7 @@ alloc_func_type(type_T *ret_type, int argcount, garray_T *type_gap) if (type == NULL) return &t_any; type->tt_type = VAR_FUNC; - type->tt_member = ret_type; + type->tt_member = ret_type == NULL ? &t_unknown : ret_type; type->tt_argcount = argcount; type->tt_args = NULL; return type; @@ -188,7 +188,7 @@ get_func_type(type_T *ret_type, int argcount, garray_T *type_gap) // recognize commonly used types if (argcount <= 0) { - if (ret_type == &t_unknown) + if (ret_type == &t_unknown || ret_type == NULL) { // (argcount == 0) is not possible return &t_func_unknown;