summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/GvimExt/gvimext.cpp19
-rw-r--r--src/Make_ami.mak1
-rw-r--r--src/Make_cyg_ming.mak1
-rw-r--r--src/Make_mvc.mak4
-rw-r--r--src/Make_vms.mms6
-rw-r--r--src/Makefile21
-rw-r--r--src/README.md1
-rwxr-xr-xsrc/auto/configure57
-rw-r--r--src/autocmd.c3
-rw-r--r--src/buffer.c1
-rw-r--r--src/bufwrite.c2
-rw-r--r--src/charset.c31
-rw-r--r--src/cmdexpand.c22
-rw-r--r--src/cmdhist.c12
-rw-r--r--src/configure.ac13
-rw-r--r--src/dict.c27
-rw-r--r--src/drawline.c62
-rw-r--r--src/drawscreen.c2
-rw-r--r--src/edit.c11
-rw-r--r--src/eval.c3569
-rw-r--r--src/evalfunc.c470
-rw-r--r--src/ex_cmds.c32
-rw-r--r--src/ex_cmds.h12
-rw-r--r--src/ex_docmd.c16
-rw-r--r--src/ex_getln.c10
-rw-r--r--src/fileio.c75
-rw-r--r--src/filepath.c35
-rw-r--r--src/gc.c780
-rw-r--r--src/globals.h1
-rw-r--r--src/gui.c4
-rw-r--r--src/gui_gtk_x11.c20
-rw-r--r--src/gui_x11.c11
-rw-r--r--src/highlight.c2
-rw-r--r--src/if_cscope.c2
-rw-r--r--src/if_py_both.h7
-rw-r--r--src/indent.c47
-rw-r--r--src/insexpand.c251
-rwxr-xr-xsrc/link.sh2
-rw-r--r--src/list.c24
-rw-r--r--src/misc1.c13
-rw-r--r--src/mouse.c22
-rw-r--r--src/move.c67
-rw-r--r--src/normal.c152
-rw-r--r--src/ops.c54
-rw-r--r--src/option.c23
-rw-r--r--src/option.h15
-rw-r--r--src/optiondefs.h7
-rw-r--r--src/optionstr.c19
-rw-r--r--src/os_unix.h2
-rw-r--r--src/po/check.vim4
-rw-r--r--src/po/it.po83
-rw-r--r--src/popupmenu.c276
-rw-r--r--src/popupwin.c4
-rw-r--r--src/proto.h1
-rw-r--r--src/proto/cmdexpand.pro2
-rw-r--r--src/proto/cmdhist.pro2
-rw-r--r--src/proto/dict.pro1
-rw-r--r--src/proto/eval.pro10
-rw-r--r--src/proto/ex_docmd.pro4
-rw-r--r--src/proto/fileio.pro1
-rw-r--r--src/proto/filepath.pro3
-rw-r--r--src/proto/gc.pro12
-rw-r--r--src/proto/indent.pro6
-rw-r--r--src/proto/insexpand.pro3
-rw-r--r--src/proto/list.pro1
-rw-r--r--src/proto/misc1.pro2
-rw-r--r--src/proto/normal.pro1
-rw-r--r--src/proto/option.pro2
-rw-r--r--src/proto/search.pro9
-rw-r--r--src/proto/spell.pro2
-rw-r--r--src/proto/vim9class.pro2
-rw-r--r--src/quickfix.c9
-rw-r--r--src/regexp.c413
-rw-r--r--src/regexp_bt.c39
-rw-r--r--src/regexp_nfa.c5
-rw-r--r--src/scriptfile.c7
-rw-r--r--src/search.c345
-rw-r--r--src/spell.c11
-rw-r--r--src/spellsuggest.c2
-rw-r--r--src/strings.c7
-rw-r--r--src/structs.h5
-rw-r--r--src/tag.c14
-rw-r--r--src/terminal.c19
-rw-r--r--src/testdir/Make_all.mak4
-rw-r--r--src/testdir/dumps/Test_balloon_eval_term_03.dump10
-rw-r--r--src/testdir/dumps/Test_mouse_popup_position_01.dump20
-rw-r--r--src/testdir/dumps/Test_mouse_popup_position_02.dump20
-rw-r--r--src/testdir/dumps/Test_popup_command_rl.dump20
-rw-r--r--src/testdir/dumps/Test_pum_highlights_03.dump20
-rw-r--r--src/testdir/dumps/Test_pum_highlights_04.dump20
-rw-r--r--src/testdir/dumps/Test_pum_highlights_05.dump20
-rw-r--r--src/testdir/dumps/Test_pum_highlights_06.dump20
-rw-r--r--src/testdir/dumps/Test_pum_highlights_06a.dump20
-rw-r--r--src/testdir/dumps/Test_pum_highlights_06b.dump20
-rw-r--r--src/testdir/dumps/Test_pum_highlights_07.dump20
-rw-r--r--src/testdir/dumps/Test_pum_highlights_08.dump20
-rw-r--r--src/testdir/dumps/Test_pum_highlights_09.dump20
-rw-r--r--src/testdir/dumps/Test_smooth_long_6.dump2
-rw-r--r--src/testdir/dumps/Test_smooth_long_7.dump2
-rw-r--r--src/testdir/dumps/Test_smooth_long_scrolloff_1.dump8
-rw-r--r--src/testdir/dumps/Test_smooth_long_scrolloff_2.dump8
-rw-r--r--src/testdir/dumps/Test_smooth_long_scrolloff_3.dump8
-rw-r--r--src/testdir/dumps/Test_smooth_long_scrolloff_4.dump8
-rw-r--r--src/testdir/dumps/Test_smooth_long_scrolloff_5.dump8
-rw-r--r--src/testdir/dumps/Test_smooth_long_scrolloff_6.dump8
-rw-r--r--src/testdir/dumps/Test_smooth_long_scrolloff_7.dump8
-rw-r--r--src/testdir/dumps/Test_smoothscroll_in_qf_window_1.dump20
-rw-r--r--src/testdir/dumps/Test_smoothscroll_in_qf_window_2.dump20
-rw-r--r--src/testdir/dumps/Test_smoothscroll_in_qf_window_3.dump20
-rw-r--r--src/testdir/dumps/Test_smoothscroll_in_qf_window_4.dump20
-rw-r--r--src/testdir/dumps/Test_smoothscroll_in_qf_window_5.dump20
-rw-r--r--src/testdir/dumps/Test_wildmenu_pum_hl_match_1.dump10
-rw-r--r--src/testdir/dumps/Test_wildmenu_pum_hl_match_2.dump10
-rw-r--r--src/testdir/dumps/Test_wildmenu_pum_hl_match_3.dump10
-rw-r--r--src/testdir/dumps/Test_wildmenu_pum_hl_match_4.dump10
-rw-r--r--src/testdir/dumps/Test_wildmenu_pum_hl_match_5.dump10
-rw-r--r--src/testdir/dumps/Test_wildmenu_pum_hl_match_6.dump10
-rw-r--r--src/testdir/dumps/Test_wildmenu_pum_rl.dump10
-rw-r--r--src/testdir/gen_opt_test.vim2
-rw-r--r--src/testdir/test_autocmd.vim26
-rw-r--r--src/testdir/test_autoload.vim1
-rw-r--r--src/testdir/test_balloon.vim25
-rw-r--r--src/testdir/test_blob.vim32
-rw-r--r--src/testdir/test_cmdline.vim49
-rw-r--r--src/testdir/test_edit.vim5
-rw-r--r--src/testdir/test_filecopy.vim72
-rw-r--r--src/testdir/test_filetype.vim59
-rw-r--r--src/testdir/test_fold.vim127
-rw-r--r--src/testdir/test_functions.vim96
-rw-r--r--src/testdir/test_global.vim11
-rw-r--r--src/testdir/test_gui.vim2
-rw-r--r--src/testdir/test_history.vim54
-rw-r--r--src/testdir/test_ins_complete.vim184
-rw-r--r--src/testdir/test_listdict.vim21
-rw-r--r--src/testdir/test_method.vim7
-rw-r--r--src/testdir/test_normal.vim130
-rw-r--r--src/testdir/test_partial.vim14
-rw-r--r--src/testdir/test_popup.vim149
-rw-r--r--src/testdir/test_quickfix.vim70
-rw-r--r--src/testdir/test_remote.vim2
-rw-r--r--src/testdir/test_scroll_opt.vim80
-rw-r--r--src/testdir/test_shell.vim4
-rw-r--r--src/testdir/test_shortpathname.vim22
-rw-r--r--src/testdir/test_spellrare.vim61
-rw-r--r--src/testdir/test_substitute.vim24
-rw-r--r--src/testdir/test_tagjump.vim18
-rw-r--r--src/testdir/test_termdebug.vim118
-rw-r--r--src/testdir/test_terminal3.vim25
-rw-r--r--src/testdir/test_textformat.vim2
-rw-r--r--src/testdir/test_vartabs.vim59
-rw-r--r--src/testdir/test_vim9_assign.vim14
-rw-r--r--src/testdir/test_vim9_builtin.vim28
-rw-r--r--src/testdir/test_vim9_class.vim54
-rw-r--r--src/testdir/test_vim9_expr.vim16
-rw-r--r--src/testdir/test_vim9_func.vim13
-rw-r--r--src/testdir/test_vim9_import.vim110
-rw-r--r--src/testdir/test_vim9_script.vim88
-rw-r--r--src/testdir/test_vim9_typealias.vim2
-rw-r--r--src/testdir/test_viminfo.vim30
-rw-r--r--src/testdir/test_vimscript.vim8
-rw-r--r--src/testdir/test_virtualedit.vim22
-rw-r--r--src/testdir/test_visual.vim781
-rw-r--r--src/testdir/test_xdg.vim4
-rw-r--r--src/testdir/test_xxd.vim16
-rw-r--r--src/testing.c2
-rw-r--r--src/userfunc.c4
-rw-r--r--src/version.c238
-rw-r--r--src/vim.h13
-rw-r--r--src/vim9class.c4
-rw-r--r--src/vim9type.c15
-rw-r--r--src/xxd/xxd.c34
171 files changed, 7979 insertions, 2899 deletions
diff --git a/src/GvimExt/gvimext.cpp b/src/GvimExt/gvimext.cpp
index e58b142..f98423c 100644
--- a/src/GvimExt/gvimext.cpp
+++ b/src/GvimExt/gvimext.cpp
@@ -55,9 +55,11 @@ getGvimName(char *name, int runtime)
HKEY keyhandle;
DWORD hlen;
- // Get the location of gvim from the registry.
+ // Get the location of gvim from the registry. Try
+ // HKEY_CURRENT_USER first, then fall back to HKEY_LOCAL_MACHINE if
+ // not found.
name[0] = 0;
- if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0,
+ if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Vim\\Gvim", 0,
KEY_READ, &keyhandle) == ERROR_SUCCESS)
{
hlen = BUFSIZE;
@@ -69,6 +71,19 @@ getGvimName(char *name, int runtime)
RegCloseKey(keyhandle);
}
+ if ((name[0] == 0) &&
+ (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0,
+ KEY_READ, &keyhandle) == ERROR_SUCCESS))
+ {
+ hlen = BUFSIZE;
+ if (RegQueryValueEx(keyhandle, "path", 0, NULL, (BYTE *)name, &hlen)
+ != ERROR_SUCCESS)
+ name[0] = 0;
+ else
+ name[hlen] = 0;
+ RegCloseKey(keyhandle);
+ }
+
// Registry didn't work, use the search path.
if (name[0] == 0)
strcpy(name, searchpath((char *)"gvim.exe"));
diff --git a/src/Make_ami.mak b/src/Make_ami.mak
index 9e9ebe3..09cdd3c 100644
--- a/src/Make_ami.mak
+++ b/src/Make_ami.mak
@@ -114,6 +114,7 @@ SRC += \
float.c \
fold.c \
getchar.c \
+ gc.c \
hardcopy.c \
hashtab.c \
help.c \
diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak
index 7afb6e0..20ed903 100644
--- a/src/Make_cyg_ming.mak
+++ b/src/Make_cyg_ming.mak
@@ -797,6 +797,7 @@ OBJ = \
$(OUTDIR)/float.o \
$(OUTDIR)/fold.o \
$(OUTDIR)/getchar.o \
+ $(OUTDIR)/gc.o \
$(OUTDIR)/gui_xim.o \
$(OUTDIR)/hardcopy.o \
$(OUTDIR)/hashtab.o \
diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak
index 4d03a72..e58814b 100644
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -718,6 +718,7 @@ OBJ = \
$(OUTDIR)\float.obj \
$(OUTDIR)\fold.obj \
$(OUTDIR)\getchar.obj \
+ $(OUTDIR)\gc.obj \
$(OUTDIR)\gui_xim.obj \
$(OUTDIR)\hardcopy.obj \
$(OUTDIR)\hashtab.obj \
@@ -1575,6 +1576,8 @@ $(OUTDIR)/fold.obj: $(OUTDIR) fold.c $(INCL)
$(OUTDIR)/getchar.obj: $(OUTDIR) getchar.c $(INCL)
+$(OUTDIR)/gc.obj: $(OUTDIR) gc.c $(INCL)
+
$(OUTDIR)/gui_xim.obj: $(OUTDIR) gui_xim.c $(INCL)
$(OUTDIR)/hardcopy.obj: $(OUTDIR) hardcopy.c $(INCL) version.h
@@ -1915,6 +1918,7 @@ proto.h: \
proto/findfile.pro \
proto/float.pro \
proto/getchar.pro \
+ proto/gc.pro \
proto/gui_xim.pro \
proto/hardcopy.pro \
proto/hashtab.pro \
diff --git a/src/Make_vms.mms b/src/Make_vms.mms
index f050c9d..559c2f8 100644
--- a/src/Make_vms.mms
+++ b/src/Make_vms.mms
@@ -369,6 +369,7 @@ SRC = \
float.c \
fold.c \
getchar.c \
+ gc.c \
gui_xim.c \
hardcopy.c \
hashtab.c \
@@ -500,6 +501,7 @@ OBJ = \
float.obj \
fold.obj \
getchar.obj \
+ gc.obj \
gui_xim.obj \
hardcopy.obj \
hashtab.obj \
@@ -942,6 +944,10 @@ getchar.obj : getchar.c vim.h [.auto]config.h feature.h os_unix.h \
ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
errors.h globals.h
+gc.obj : gc.c vim.h [.auto]config.h feature.h os_unix.h \
+ ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
+ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
+ errors.h globals.h
gui_xim.obj : gui_xim.c vim.h [.auto]config.h feature.h os_unix.h \
ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \
gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \
diff --git a/src/Makefile b/src/Makefile
index 29f63dc..71578eb 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1520,6 +1520,7 @@ BASIC_SRC = \
float.c \
fold.c \
getchar.c \
+ gc.c \
gui_xim.c \
hardcopy.c \
hashtab.c \
@@ -1682,6 +1683,7 @@ OBJ_COMMON = \
objects/float.o \
objects/fold.o \
objects/getchar.o \
+ objects/gc.o \
objects/gui_xim.o \
objects/hardcopy.o \
objects/hashtab.o \
@@ -1860,6 +1862,7 @@ PRO_AUTO = \
float.pro \
fold.pro \
getchar.pro \
+ gc.pro \
gui_xim.pro \
gui_beval.pro \
hardcopy.pro \
@@ -2340,7 +2343,7 @@ installrtbase: $(HELPSOURCE)/vim.1 $(DEST_VIM) $(VIMTARGET) $(DEST_RT) \
$(DEST_HELP) $(DEST_PRINT) $(DEST_COL) \
$(DEST_SYN) $(DEST_SYN)/modula2 $(DEST_SYN)/modula2/opt $(DEST_SYN)/shared \
$(DEST_IND) $(DEST_FTP) \
- $(DEST_AUTO) $(DEST_AUTO)/dist $(DEST_AUTO)/xml $(DEST_AUTO)/zig \
+ $(DEST_AUTO) $(DEST_AUTO)/dist $(DEST_AUTO)/xml \
$(DEST_AUTO)/rust $(DEST_AUTO)/cargo \
$(DEST_IMPORT) $(DEST_IMPORT)/dist \
$(DEST_PLUG) $(DEST_TUTOR) $(DEST_SPELL) $(DEST_COMP)
@@ -2427,8 +2430,6 @@ installrtbase: $(HELPSOURCE)/vim.1 $(DEST_VIM) $(VIMTARGET) $(DEST_RT) \
cd $(DEST_AUTO)/dist; chmod $(HELPMOD) *.vim
cd $(AUTOSOURCE)/xml; $(INSTALL_DATA) *.vim $(DEST_AUTO)/xml
cd $(DEST_AUTO)/xml; chmod $(HELPMOD) *.vim
- cd $(AUTOSOURCE)/zig; $(INSTALL_DATA) *.vim $(DEST_AUTO)/zig
- cd $(DEST_AUTO)/zig; chmod $(HELPMOD) *.vim
cd $(AUTOSOURCE)/cargo; $(INSTALL_DATA) *.vim $(DEST_AUTO)/cargo
cd $(DEST_AUTO)/cargo; chmod $(HELPMOD) *.vim
cd $(AUTOSOURCE)/rust; $(INSTALL_DATA) *.vim $(DEST_AUTO)/rust
@@ -2673,7 +2674,7 @@ $(DESTDIR)$(exec_prefix) $(DEST_BIN) \
$(DEST_IND) $(DEST_FTP) \
$(DEST_LANG) $(DEST_KMAP) $(DEST_COMP) $(DEST_MACRO) \
$(DEST_PACK) $(DEST_TOOLS) $(DEST_TUTOR) $(DEST_SPELL) \
- $(DEST_AUTO) $(DEST_AUTO)/dist $(DEST_AUTO)/xml $(DEST_AUTO)/zig \
+ $(DEST_AUTO) $(DEST_AUTO)/dist $(DEST_AUTO)/xml \
$(DEST_AUTO)/cargo $(DEST_AUTO)/rust \
$(DEST_IMPORT) $(DEST_IMPORT)/dist $(DEST_PLUG):
$(MKDIR_P) $@
@@ -2865,10 +2866,10 @@ uninstall_runtime:
-rmdir $(DEST_SYN) $(DEST_IND)
-rm -rf $(DEST_FTP)/*.vim $(DEST_FTP)/README.txt $(DEST_FTP)/logtalk.dict
-rm -f $(DEST_AUTO)/*.vim $(DEST_AUTO)/README.txt
- -rm -f $(DEST_AUTO)/dist/*.vim $(DEST_AUTO)/xml/*.vim $(DEST_AUTO)/zig/*.vim $(DEST_AUTO)/cargo/*.vim $(DEST_AUTO)/rust/*.vim
+ -rm -f $(DEST_AUTO)/dist/*.vim $(DEST_AUTO)/xml/*.vim $(DEST_AUTO)/cargo/*.vim $(DEST_AUTO)/rust/*.vim
-rm -f $(DEST_IMPORT)/dist/*.vim
-rm -f $(DEST_PLUG)/*.vim $(DEST_PLUG)/README.txt
- -rmdir $(DEST_FTP) $(DEST_AUTO)/dist $(DEST_AUTO)/xml $(DEST_AUTO)/zig $(DEST_AUTO)/cargo $(DEST_AUTO)/rust $(DEST_AUTO)
+ -rmdir $(DEST_FTP) $(DEST_AUTO)/dist $(DEST_AUTO)/xml $(DEST_AUTO)/cargo $(DEST_AUTO)/rust $(DEST_AUTO)
-rmdir $(DEST_IMPORT)/dist $(DEST_IMPORT)
-rmdir $(DEST_PLUG) $(DEST_RT)
# This will fail when other Vim versions are installed, no worries.
@@ -3224,6 +3225,9 @@ objects/fold.o: fold.c
objects/getchar.o: getchar.c
$(CCC) -o $@ getchar.c
+objects/gc.o: gc.c
+ $(CCC) -o $@ gc.c
+
objects/hardcopy.o: hardcopy.c
$(CCC) -o $@ hardcopy.c
@@ -3875,6 +3879,11 @@ objects/getchar.o: getchar.c vim.h protodef.h auto/config.h feature.h os_unix.h
proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
globals.h errors.h
+objects/gc.o: gc.c vim.h protodef.h auto/config.h feature.h os_unix.h \
+ auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
+ proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
+ libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
+ globals.h errors.h
objects/gui_xim.o: gui_xim.c vim.h protodef.h auto/config.h feature.h os_unix.h \
auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
diff --git a/src/README.md b/src/README.md
index f3806d2..4042988 100644
--- a/src/README.md
+++ b/src/README.md
@@ -49,6 +49,7 @@ filepath.c | dealing with file names and paths
findfile.c | search for files in 'path'
fold.c | folding
getchar.c | getting characters and key mapping
+gc.c | garbage collection
help.c | vim help related functions
highlight.c | syntax highlighting
indent.c | text indentation
diff --git a/src/auto/configure b/src/auto/configure
index 0e0cf8e..98b9580 100755
--- a/src/auto/configure
+++ b/src/auto/configure
@@ -6502,11 +6502,13 @@ printf "%s\n" "$vi_cv_perl_xsubpp" >&6; }
-e 's/-flto\(=auto\)\? //' \
-e 's/-W[^ ]*//g' \
-e 's/-D_FORTIFY_SOURCE=.//g'`
- perllibs=`cd $srcdir; $vi_cv_path_perl -MExtUtils::Embed -e 'ldopts' | \
+ perllibs=`cd $srcdir; $vi_cv_path_perl -MExtUtils::Embed -e 'ldopts' | \
sed -e '/Warning/d' -e '/Note (probably harmless)/d' \
+ -e 's/-specs=[^ ]*//g' \
-e 's/-bE:perl.exp//' -e 's/-lc //'`
- perlldflags=`cd $srcdir; $vi_cv_path_perl -MExtUtils::Embed \
- -e 'ccdlflags' | sed -e 's/-bE:perl.exp//'`
+ perlldflags=`cd $srcdir; $vi_cv_path_perl -MExtUtils::Embed \
+ -e 'ccdlflags' | sed -e 's/-bE:perl.exp//' \
+ -e 's/-specs=[^ ]*//g' `
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if compile and link flags for Perl are sane" >&5
printf %s "checking if compile and link flags for Perl are sane... " >&6; }
@@ -11405,55 +11407,6 @@ printf "%s\n" "no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-ac_fn_c_check_header_compile "$LINENO" "elf.h" "ac_cv_header_elf_h" "$ac_includes_default"
-if test "x$ac_cv_header_elf_h" = xyes
-then :
- HAS_ELF=1
-fi
-
-if test "$HAS_ELF" = 1; then
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for main in -lelf" >&5
-printf %s "checking for main in -lelf... " >&6; }
-if test ${ac_cv_lib_elf_main+y}
-then :
- printf %s "(cached) " >&6
-else $as_nop
- ac_check_lib_save_LIBS=$LIBS
-LIBS="-lelf $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-
-
-int
-main (void)
-{
-return main ();
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"
-then :
- ac_cv_lib_elf_main=yes
-else $as_nop
- ac_cv_lib_elf_main=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
- conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_elf_main" >&5
-printf "%s\n" "$ac_cv_lib_elf_main" >&6; }
-if test "x$ac_cv_lib_elf_main" = xyes
-then :
- printf "%s\n" "#define HAVE_LIBELF 1" >>confdefs.h
-
- LIBS="-lelf $LIBS"
-
-fi
-
-fi
-
ac_header_dirent=no
for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
as_ac_Header=`printf "%s\n" "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
diff --git a/src/autocmd.c b/src/autocmd.c
index bce57cb..8380f8a 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -3391,6 +3391,9 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv)
{
char_u *group_name;
+ if (ap->pat == NULL) // pattern has been removed
+ continue;
+
if (group != AUGROUP_ALL && group != ap->group)
continue;
diff --git a/src/buffer.c b/src/buffer.c
index 58e9718..cbec9b9 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -2429,6 +2429,7 @@ free_buf_options(
clear_string_option(&buf->b_p_lop);
clear_string_option(&buf->b_p_cinsd);
clear_string_option(&buf->b_p_cinw);
+ clear_string_option(&buf->b_p_cot);
clear_string_option(&buf->b_p_cpt);
#ifdef FEAT_COMPL_FUNC
clear_string_option(&buf->b_p_cfu);
diff --git a/src/bufwrite.c b/src/bufwrite.c
index bf79ad5..c9d9875 100644
--- a/src/bufwrite.c
+++ b/src/bufwrite.c
@@ -690,7 +690,7 @@ buf_write(
int write_undo_file = FALSE;
context_sha256_T sha_ctx;
#endif
- unsigned int bkc = get_bkc_value(buf);
+ unsigned int bkc = get_bkc_flags(buf);
pos_T orig_start = buf->b_op_start;
pos_T orig_end = buf->b_op_end;
diff --git a/src/charset.c b/src/charset.c
index 470698f..9aa402a 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -765,10 +765,22 @@ linetabsize_str(char_u *s)
linetabsize_col(int startcol, char_u *s)
{
chartabsize_T cts;
+ vimlong_T vcol;
init_chartabsize_arg(&cts, curwin, 0, startcol, s, s);
+ vcol = cts.cts_vcol;
+
while (*cts.cts_ptr != NUL)
- cts.cts_vcol += lbr_chartabsize_adv(&cts);
+ {
+ vcol += lbr_chartabsize_adv(&cts);
+ if (vcol > MAXCOL)
+ {
+ cts.cts_vcol = MAXCOL;
+ break;
+ }
+ else
+ cts.cts_vcol = (int)vcol;
+ }
clear_chartabsize_arg(&cts);
return (int)cts.cts_vcol;
}
@@ -840,22 +852,33 @@ linetabsize_no_outer(win_T *wp, linenr_T lnum)
void
win_linetabsize_cts(chartabsize_T *cts, colnr_T len)
{
+ vimlong_T vcol = cts->cts_vcol;
#ifdef FEAT_PROP_POPUP
cts->cts_with_trailing = len == MAXCOL;
#endif
for ( ; *cts->cts_ptr != NUL && (len == MAXCOL || cts->cts_ptr < cts->cts_line + len);
MB_PTR_ADV(cts->cts_ptr))
- cts->cts_vcol += win_lbr_chartabsize(cts, NULL);
+ {
+ vcol += win_lbr_chartabsize(cts, NULL);
+ if (vcol > MAXCOL)
+ {
+ cts->cts_vcol = MAXCOL;
+ break;
+ }
+ else
+ cts->cts_vcol = (int)vcol;
+ }
#ifdef FEAT_PROP_POPUP
// check for a virtual text at the end of a line or on an empty line
if (len == MAXCOL && cts->cts_has_prop_with_text && *cts->cts_ptr == NUL)
{
(void)win_lbr_chartabsize(cts, NULL);
- cts->cts_vcol += cts->cts_cur_text_width;
+ vcol += cts->cts_cur_text_width;
// when properties are above or below the empty line must also be
// counted
if (cts->cts_ptr == cts->cts_line && cts->cts_prop_lines > 0)
- ++cts->cts_vcol;
+ ++vcol;
+ cts->cts_vcol = vcol > MAXCOL ? MAXCOL : (int)vcol;
}
#endif
}
diff --git a/src/cmdexpand.c b/src/cmdexpand.c
index 1008bf9..8d8bf06 100644
--- a/src/cmdexpand.c
+++ b/src/cmdexpand.c
@@ -438,6 +438,28 @@ cmdline_compl_startcol(void)
}
/*
+ * Returns the current cmdline completion pattern.
+ */
+ char_u *
+cmdline_compl_pattern(void)
+{
+ expand_T *xp = get_cmdline_info()->xpc;
+
+ return xp == NULL ? NULL : xp->xp_orig;
+}
+
+/*
+ * Returns TRUE if fuzzy cmdline completion is active, FALSE otherwise.
+ */
+ int
+cmdline_compl_is_fuzzy(void)
+{
+ expand_T *xp = get_cmdline_info()->xpc;
+
+ return xp != NULL && cmdline_fuzzy_completion_supported(xp);
+}
+
+/*
* Return the number of characters that should be skipped in a status match.
* These are backslashes used for escaping. Do show backslashes in help tags.
*/
diff --git a/src/cmdhist.c b/src/cmdhist.c
index 6342f02..684c08e 100644
--- a/src/cmdhist.c
+++ b/src/cmdhist.c
@@ -297,11 +297,11 @@ static int last_maptick = -1; // last seen maptick
add_to_history(
int histype,
char_u *new_entry,
+ size_t new_entrylen,
int in_map, // consider maptick when inside a mapping
int sep) // separator character used (search hist)
{
histentry_T *hisptr;
- int len;
if (hislen == 0) // no history
return;
@@ -336,10 +336,9 @@ add_to_history(
vim_free(hisptr->hisstr);
// Store the separator after the NUL of the string.
- len = (int)STRLEN(new_entry);
- hisptr->hisstr = vim_strnsave(new_entry, len + 2);
+ hisptr->hisstr = vim_strnsave(new_entry, new_entrylen + 2);
if (hisptr->hisstr != NULL)
- hisptr->hisstr[len + 1] = sep;
+ hisptr->hisstr[new_entrylen + 1] = sep;
hisptr->hisnum = ++hisnum[histype];
hisptr->viminfo = FALSE;
@@ -566,7 +565,7 @@ f_histadd(typval_T *argvars UNUSED, typval_T *rettv)
return;
init_history();
- add_to_history(histype, str, FALSE, NUL);
+ add_to_history(histype, str, STRLEN(str), FALSE, NUL);
rettv->vval.v_number = TRUE;
}
@@ -768,7 +767,8 @@ ex_history(exarg_T *eap)
if (i == hislen)
i = 0;
if (hist[i].hisstr != NULL
- && hist[i].hisnum >= j && hist[i].hisnum <= k)
+ && hist[i].hisnum >= j && hist[i].hisnum <= k
+ && !message_filtered(hist[i].hisstr))
{
msg_putchar('\n');
sprintf((char *)IObuff, "%c%6d ", i == idx ? '>' : ' ',
diff --git a/src/configure.ac b/src/configure.ac
index f6e54b3..946fe52 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -1173,13 +1173,17 @@ if test "$enable_perlinterp" = "yes" -o "$enable_perlinterp" = "dynamic"; then
-e 's/-W[[^ ]]*//g' \
-e 's/-D_FORTIFY_SOURCE=.//g'`
dnl Remove "-lc", it breaks on FreeBSD when using "-pthread".
+ dnl Remove -specs=<file-path>, the hardened flags cause relocation errors
perllibs=`cd $srcdir; $vi_cv_path_perl -MExtUtils::Embed -e 'ldopts' | \
sed -e '/Warning/d' -e '/Note (probably harmless)/d' \
+ -e 's/-specs=[[^ ]*]//g' \
-e 's/-bE:perl.exp//' -e 's/-lc //'`
dnl Don't add perl lib to $LIBS: if it's not in LD_LIBRARY_PATH
dnl a test in configure may fail because of that.
+ dnl Remove -specs=<file-path>, the hardened flags cause relocation errors
perlldflags=`cd $srcdir; $vi_cv_path_perl -MExtUtils::Embed \
- -e 'ccdlflags' | sed -e 's/-bE:perl.exp//'`
+ -e 'ccdlflags' | sed -e 's/-bE:perl.exp//' \
+ -e 's/-specs=[[^ ]*]//g' `
dnl check that compiling a simple program still works with the flags
dnl added for Perl.
@@ -3303,13 +3307,6 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [int x __attribute__((u
AC_MSG_RESULT(yes); AC_DEFINE(HAVE_ATTRIBUTE_UNUSED),
AC_MSG_RESULT(no))
-dnl Checks for header files.
-AC_CHECK_HEADER(elf.h, HAS_ELF=1)
-dnl AC_CHECK_HEADER(dwarf.h, SVR4=1)
-if test "$HAS_ELF" = 1; then
- AC_CHECK_LIB(elf, main)
-fi
-
AC_HEADER_DIRENT
dnl If sys/wait.h is not found it might still exist but not be POSIX
diff --git a/src/dict.c b/src/dict.c
index c78995d..bf45b0b 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -1092,6 +1092,33 @@ failret:
}
/*
+ * Evaluate a literal dictionary: #{key: val, key: val}
+ * "*arg" points to the "#".
+ * On return, "*arg" points to the character after the Dict.
+ * Return OK or FAIL. Returns NOTDONE for {expr}.
+ */
+ int
+eval_lit_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
+{
+ int vim9script = in_vim9script();
+ int ret = OK;
+
+ if (vim9script)
+ {
+ ret = vim9_bad_comment(*arg) ? FAIL : NOTDONE;
+ }
+ else if ((*arg)[1] == '{')
+ {
+ ++*arg;
+ ret = eval_dict(arg, rettv, evalarg, TRUE);
+ }
+ else
+ ret = NOTDONE;
+
+ return ret;
+}
+
+/*
* Go over all entries in "d2" and add them to "d1".
* When "action" is "error" then a duplicate key is an error.
* When "action" is "force" then a duplicate key is overwritten.
diff --git a/src/drawline.c b/src/drawline.c
index 81577be..e388bdb 100644
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -296,10 +296,9 @@ get_sign_display_info(
if (nrcol)
{
wlv->c_extra = NUL;
- sprintf((char *)wlv->extra, "%-*c ",
- number_width(wp), SIGN_BYTE);
+ wlv->n_extra = vim_snprintf((char *)wlv->extra, sizeof(wlv->extra),
+ "%-*c ", number_width(wp), SIGN_BYTE);
wlv->p_extra = wlv->extra;
- wlv->n_extra = (int)STRLEN(wlv->p_extra);
}
else
wlv->c_extra = SIGN_BYTE;
@@ -310,10 +309,9 @@ get_sign_display_info(
if (nrcol)
{
wlv->c_extra = NUL;
- sprintf((char *)wlv->extra, "%-*c ", number_width(wp),
- MULTISIGN_BYTE);
+ wlv->n_extra = vim_snprintf((char *)wlv->extra, sizeof(wlv->extra),
+ "%-*c ", number_width(wp), MULTISIGN_BYTE);
wlv->p_extra = wlv->extra;
- wlv->n_extra = (int)STRLEN(wlv->p_extra);
}
else
wlv->c_extra = MULTISIGN_BYTE;
@@ -332,17 +330,18 @@ get_sign_display_info(
if (nrcol)
{
int width = number_width(wp) - 2;
- int n;
- for (n = 0; n < width; n++)
- wlv->extra[n] = ' ';
- vim_snprintf((char *)wlv->extra + n,
- sizeof(wlv->extra) - n, "%s ", wlv->p_extra);
+ vim_memset(wlv->extra, ' ', width);
+ wlv->n_extra = width;
+ wlv->n_extra += vim_snprintf((char *)wlv->extra + width,
+ sizeof(wlv->extra) - width, "%s ", wlv->p_extra);
wlv->p_extra = wlv->extra;
}
+ else
+ wlv->n_extra = (int)STRLEN(wlv->p_extra);
+
wlv->c_extra = NUL;
wlv->c_final = NUL;
- wlv->n_extra = (int)STRLEN(wlv->p_extra);
}
if (use_cursor_line_highlight(wp, wlv->lnum)
@@ -417,7 +416,7 @@ handle_lnum_col(
}
}
- sprintf((char *)wlv->extra, fmt, number_width(wp), num);
+ vim_snprintf((char *)wlv->extra, sizeof(wlv->extra), fmt, number_width(wp), num);
if (wp->w_skipcol > 0 && wlv->startrow == 0)
for (wlv->p_extra = wlv->extra; *wlv->p_extra == ' ';
++wlv->p_extra)
@@ -621,25 +620,26 @@ textprop_size_after_trunc(
{
int space = (flags & (TP_FLAG_ALIGN_BELOW | TP_FLAG_ALIGN_ABOVE))
? wp->w_width - win_col_off(wp) : added;
- int len = (int)STRLEN(text);
int strsize = 0;
- int n_used;
+ char_u *p;
- // if the remaining size is to small and 'wrap' is set we wrap anyway and
+ // if the remaining size is too small and 'wrap' is set we wrap anyway and
// use the next line
if (space < PROP_TEXT_MIN_CELLS && wp->w_p_wrap)
space += wp->w_width;
if (flags & (TP_FLAG_ALIGN_BELOW | TP_FLAG_ALIGN_ABOVE))
space -= padding;
- for (n_used = 0; n_used < len; n_used += (*mb_ptr2len)(text + n_used))
+
+ for (p = text; *p != NUL; p += (*mb_ptr2len)(p))
{
- int clen = ptr2cells(text + n_used);
+ int clen = ptr2cells(p);
if (strsize + clen > space)
break;
strsize += clen;
}
- *n_used_ptr = n_used;
+ *n_used_ptr = (int)(p - text);
+
return strsize;
}
@@ -1801,13 +1801,13 @@ win_line(
pos = wp->w_cursor;
wp->w_cursor.lnum = lnum;
wp->w_cursor.col = linecol;
- len = spell_move_to(wp, FORWARD, TRUE, TRUE, &spell_hlf);
+ len = spell_move_to(wp, FORWARD, SMT_ALL, TRUE, &spell_hlf);
// spell_move_to() may call ml_get() and make "line" invalid
line = ml_get_buf(wp->w_buffer, lnum, FALSE);
ptr = line + linecol;
- if (len == 0 || (int)wp->w_cursor.col > ptr - line)
+ if (len == 0 || (int)wp->w_cursor.col > linecol)
{
// no bad word found at line start, don't check until end of a
// word
@@ -2821,15 +2821,16 @@ win_line(
// head byte at end of line
mb_l = 1;
transchar_nonprint(wp->w_buffer, wlv.extra, c);
+ wlv.n_extra = (int)STRLEN(wlv.extra) - 1;
}
else
{
// illegal tail byte
mb_l = 2;
STRCPY(wlv.extra, "XX");
+ wlv.n_extra = 1;
}
wlv.p_extra = wlv.extra;
- wlv.n_extra = (int)STRLEN(wlv.extra) - 1;
wlv.c_extra = NUL;
wlv.c_final = NUL;
c = *wlv.p_extra++;
@@ -3388,11 +3389,16 @@ win_line(
c = *wlv.p_extra;
p = alloc(wlv.n_extra + 1);
- vim_memset(p, ' ', wlv.n_extra);
- STRNCPY(p, wlv.p_extra + 1, STRLEN(wlv.p_extra) - 1);
- p[wlv.n_extra] = NUL;
- vim_free(wlv.p_extra_free);
- wlv.p_extra_free = wlv.p_extra = p;
+ if (p == NULL)
+ wlv.n_extra = 0;
+ else
+ {
+ vim_memset(p, ' ', wlv.n_extra);
+ STRNCPY(p, wlv.p_extra + 1, STRLEN(wlv.p_extra) - 1);
+ p[wlv.n_extra] = NUL;
+ vim_free(wlv.p_extra_free);
+ wlv.p_extra_free = wlv.p_extra = p;
+ }
}
else
#endif
@@ -4251,7 +4257,7 @@ win_line(
if (!wp->w_p_wrap && text_prop_follows && !text_prop_above)
{
// do not output more of the line, only the "below" prop
- ptr += STRLEN(ptr);
+ ptr = line + (size_t)ml_get_buf_len(wp->w_buffer, lnum);
# ifdef FEAT_LINEBREAK
wlv.dont_use_showbreak = TRUE;
# endif
diff --git a/src/drawscreen.c b/src/drawscreen.c
index f8818ff..9096c28 100644
--- a/src/drawscreen.c
+++ b/src/drawscreen.c
@@ -1781,7 +1781,7 @@ win_update(win_T *wp)
if (j < wp->w_height - 2) // not too far off
{
i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1,
- TRUE);
+ wp->w_height);
#ifdef FEAT_DIFF
// insert extra lines for previously invisible filler lines
if (wp->w_lines[0].wl_lnum != wp->w_topline)
diff --git a/src/edit.c b/src/edit.c
index 075b39b..e75a1cf 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -519,9 +519,10 @@ edit(
if (
#ifdef FEAT_VARTABS
- curwin->w_wcol < mincol - tabstop_at(
- get_nolist_virtcol(), curbuf->b_p_ts,
- curbuf->b_p_vts_array)
+ curwin->w_wcol < mincol - tabstop_at(get_nolist_virtcol(),
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array,
+ FALSE)
#else
(int)curwin->w_wcol < mincol - curbuf->b_p_ts
#endif
@@ -1310,7 +1311,7 @@ docomplete:
c = ins_ctrl_ey(c);
break;
- default:
+ default:
#ifdef UNIX
if (c == intr_char) // special interrupt char
goto do_intr;
@@ -1842,7 +1843,7 @@ backspace_until_column(int col)
* Only matters when there are composing characters.
* Return TRUE when something was deleted.
*/
- static int
+ static int
del_char_after_col(int limit_col UNUSED)
{
if (enc_utf8 && limit_col >= 0)
diff --git a/src/eval.c b/src/eval.c
index 51ee604..7848fea 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -22,13 +22,6 @@
#define NAMESPACE_CHAR (char_u *)"abglstvw"
-/*
- * When recursively copying lists and dicts we need to remember which ones we
- * have done to avoid endless recursiveness. This unique ID is used for that.
- * The last bit is used for previous_funccal, ignored when comparing.
- */
-static int current_copyID = 0;
-
static int eval2(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
static int eval3(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
static int eval4(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
@@ -39,7 +32,6 @@ static int eval8(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_str
static int eval9(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int want_string);
static int eval9_leader(typval_T *rettv, int numeric_only, char_u *start_leader, char_u **end_leaderp);
-static int free_unref_items(int copyID);
static char_u *make_expanded_name(char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end);
/*
@@ -250,93 +242,146 @@ eval_expr_get_funccal(typval_T *expr, typval_T *rettv)
}
/*
- * Evaluate an expression, which can be a function, partial or string.
+ * Evaluate a partial.
* Pass arguments "argv[argc]".
- * If "want_func" is TRUE treat a string as a function name, not an expression.
* "fc_arg" is from eval_expr_get_funccal() or NULL;
* Return the result in "rettv" and OK or FAIL.
*/
- int
-eval_expr_typval(
- typval_T *expr,
- int want_func,
- typval_T *argv,
- int argc,
- funccall_T *fc_arg,
- typval_T *rettv)
+ static int
+eval_expr_partial(
+ typval_T *expr,
+ typval_T *argv,
+ int argc,
+ funccall_T *fc_arg,
+ typval_T *rettv)
{
- char_u *s;
- char_u buf[NUMBUFLEN];
- funcexe_T funcexe;
+ partial_T *partial = expr->vval.v_partial;
- if (expr->v_type == VAR_PARTIAL)
+ if (partial == NULL)
+ return FAIL;
+
+ if (partial->pt_func != NULL
+ && partial->pt_func->uf_def_status != UF_NOT_COMPILED)
{
- partial_T *partial = expr->vval.v_partial;
+ funccall_T *fc = fc_arg != NULL ? fc_arg
+ : create_funccal(partial->pt_func, rettv);
+ int r;
- if (partial == NULL)
+ if (fc == NULL)
return FAIL;
- if (partial->pt_func != NULL
- && partial->pt_func->uf_def_status != UF_NOT_COMPILED)
- {
- funccall_T *fc = fc_arg != NULL ? fc_arg
- : create_funccal(partial->pt_func, rettv);
- int r;
-
- if (fc == NULL)
- return FAIL;
-
- // Shortcut to call a compiled function with minimal overhead.
- r = call_def_function(partial->pt_func, argc, argv,
- DEF_USE_PT_ARGV, partial, NULL, fc, rettv);
- if (fc_arg == NULL)
- remove_funccal();
- if (r == FAIL)
- return FAIL;
- }
- else
- {
- s = partial_name(partial);
- if (s == NULL || *s == NUL)
- return FAIL;
- CLEAR_FIELD(funcexe);
- funcexe.fe_evaluate = TRUE;
- funcexe.fe_partial = partial;
- if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL)
- return FAIL;
- }
- }
- else if (expr->v_type == VAR_INSTR)
- {
- return exe_typval_instr(expr, rettv);
+ // Shortcut to call a compiled function with minimal overhead.
+ r = call_def_function(partial->pt_func, argc, argv, DEF_USE_PT_ARGV,
+ partial, NULL, fc, rettv);
+ if (fc_arg == NULL)
+ remove_funccal();
+ if (r == FAIL)
+ return FAIL;
}
- else if (expr->v_type == VAR_FUNC || want_func)
+ else
{
- s = expr->v_type == VAR_FUNC
- ? expr->vval.v_string
- : tv_get_string_buf_chk_strict(expr, buf, in_vim9script());
+ char_u *s = partial_name(partial);
+ funcexe_T funcexe;
+
if (s == NULL || *s == NUL)
return FAIL;
+
CLEAR_FIELD(funcexe);
funcexe.fe_evaluate = TRUE;
+ funcexe.fe_partial = partial;
if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL)
return FAIL;
}
+
+ return OK;
+}
+
+/*
+ * Evaluate an expression which is a function.
+ * Pass arguments "argv[argc]".
+ * Return the result in "rettv" and OK or FAIL.
+ */
+ static int
+eval_expr_func(
+ typval_T *expr,
+ typval_T *argv,
+ int argc,
+ typval_T *rettv)
+{
+ funcexe_T funcexe;
+ char_u buf[NUMBUFLEN];
+ char_u *s;
+
+ if (expr->v_type == VAR_FUNC)
+ s = expr->vval.v_string;
else
- {
s = tv_get_string_buf_chk_strict(expr, buf, in_vim9script());
- if (s == NULL)
- return FAIL;
- s = skipwhite(s);
- if (eval1_emsg(&s, rettv, NULL) == FAIL)
- return FAIL;
- if (*skipwhite(s) != NUL) // check for trailing chars after expr
- {
- clear_tv(rettv);
- semsg(_(e_invalid_expression_str), s);
- return FAIL;
- }
+ if (s == NULL || *s == NUL)
+ return FAIL;
+
+ CLEAR_FIELD(funcexe);
+ funcexe.fe_evaluate = TRUE;
+ if (call_func(s, -1, rettv, argc, argv, &funcexe) == FAIL)
+ return FAIL;
+
+ return OK;
+}
+
+/*
+ * Evaluate an expression, which is a string.
+ * Return the result in "rettv" and OK or FAIL.
+ */
+ static int
+eval_expr_string(
+ typval_T *expr,
+ typval_T *rettv)
+{
+ char_u *s;
+ char_u buf[NUMBUFLEN];
+
+ s = tv_get_string_buf_chk_strict(expr, buf, in_vim9script());
+ if (s == NULL)
+ return FAIL;
+
+ s = skipwhite(s);
+ if (eval1_emsg(&s, rettv, NULL) == FAIL)
+ return FAIL;
+
+ if (*skipwhite(s) != NUL) // check for trailing chars after expr
+ {
+ clear_tv(rettv);
+ semsg(_(e_invalid_expression_str), s);
+ return FAIL;
}
+
+ return OK;
+}
+
+/*
+ * Evaluate an expression, which can be a function, partial or string.
+ * Pass arguments "argv[argc]".
+ * If "want_func" is TRUE treat a string as a function name, not an expression.
+ * "fc_arg" is from eval_expr_get_funccal() or NULL;
+ * Return the result in "rettv" and OK or FAIL.
+ */
+ int
+eval_expr_typval(
+ typval_T *expr,
+ int want_func,
+ typval_T *argv,
+ int argc,
+ funccall_T *fc_arg,
+ typval_T *rettv)
+{
+ if (expr->v_type == VAR_PARTIAL)
+ return eval_expr_partial(expr, argv, argc, fc_arg, rettv);
+ else if (expr->v_type == VAR_INSTR)
+ return exe_typval_instr(expr, rettv);
+ else if (expr->v_type == VAR_FUNC || want_func)
+ return eval_expr_func(expr, argv, argc, rettv);
+ else
+ return eval_expr_string(expr, rettv);
+
return OK;
}
@@ -1117,41 +1162,40 @@ get_lval_check_access(
ch_log(NULL, "LKVAR: get_lval_check_access(), cl_exec %p, cl %p, %c",
(void*)cl_exec, (void*)cl, *p);
#endif
- if (cl_exec == NULL || cl_exec != cl)
+ if (cl_exec != NULL && cl_exec == cl)
+ return OK;
+
+ char *msg = NULL;
+ switch (om->ocm_access)
{
- char *msg = NULL;
- switch (om->ocm_access)
- {
- case VIM_ACCESS_PRIVATE:
- msg = e_cannot_access_protected_variable_str;
+ case VIM_ACCESS_PRIVATE:
+ msg = e_cannot_access_protected_variable_str;
+ break;
+ case VIM_ACCESS_READ:
+ // If [idx] or .key following, read only OK.
+ if (*p == '[' || *p == '.')
break;
- case VIM_ACCESS_READ:
- // If [idx] or .key following, read only OK.
- if (*p == '[' || *p == '.')
- break;
- if ((flags & GLV_READ_ONLY) == 0)
+ if ((flags & GLV_READ_ONLY) == 0)
+ {
+ if (IS_ENUM(cl))
{
- if (IS_ENUM(cl))
- {
- if (om->ocm_type->tt_type == VAR_OBJECT)
- semsg(_(e_enumvalue_str_cannot_be_modified),
- cl->class_name, om->ocm_name);
- else
- msg = e_variable_is_not_writable_str;
- }
+ if (om->ocm_type->tt_type == VAR_OBJECT)
+ semsg(_(e_enumvalue_str_cannot_be_modified),
+ cl->class_name, om->ocm_name);
else
msg = e_variable_is_not_writable_str;
}
- break;
- case VIM_ACCESS_ALL:
- break;
- }
- if (msg != NULL)
- {
- emsg_var_cl_define(msg, om->ocm_name, 0, cl);
- return FAIL;
- }
-
+ else
+ msg = e_variable_is_not_writable_str;
+ }
+ break;
+ case VIM_ACCESS_ALL:
+ break;
+ }
+ if (msg != NULL)
+ {
+ emsg_var_cl_define(msg, om->ocm_name, 0, cl);
+ return FAIL;
}
return OK;
}
@@ -1170,12 +1214,10 @@ get_lval_check_access(
static char_u *
get_lval_imported(
lval_T *lp,
- typval_T *rettv,
scid_T imp_sid,
char_u *p,
dictitem_T **dip,
- int fne_flags,
- int vim9script)
+ int fne_flags)
{
ufunc_T *ufunc;
type_T *type = NULL;
@@ -1197,16 +1239,6 @@ get_lval_imported(
TRUE) == -1)
goto failed;
- if (vim9script && type != NULL)
- {
- where_T where = WHERE_INIT;
-
- // In a vim9 script, do type check and make sure the variable is
- // writable.
- if (check_typval_type(type, rettv, where) == FAIL)
- goto failed;
- }
-
// Get the typval for the exported item
hashtab_T *ht = &SCRIPT_VARS(imp_sid);
if (ht == NULL)
@@ -1232,6 +1264,7 @@ get_lval_imported(
goto failed;
lp->ll_tv = &di->di_tv;
+ lp->ll_valtype = type;
success:
rc = OK;
@@ -1241,6 +1274,674 @@ failed:
return rc == OK ? p : NULL;
}
+typedef enum {
+ GLV_FAIL,
+ GLV_OK,
+ GLV_STOP
+} glv_status_T;
+
+/*
+ * Get an Dict lval variable that can be assigned a value to: "name",
+ * "name[expr]", "name[expr][expr]", "name.key", "name.key[expr]" etc.
+ * "name" points to the start of the name.
+ * If "rettv" is not NULL it points to the value to be assigned.
+ * "unlet" is TRUE for ":unlet": slightly different behavior when something is
+ * wrong; must end in space or cmd separator.
+ *
+ * flags:
+ * GLV_QUIET: do not give error messages
+ * GLV_READ_ONLY: will not change the variable
+ * GLV_NO_AUTOLOAD: do not use script autoloading
+ *
+ * The Dict is returned in 'lp'. Returns GLV_OK on success and GLV_FAIL on
+ * failure. Returns GLV_STOP to stop processing the characters following
+ * 'key_end'.
+ */
+ static int
+get_lval_dict_item(
+ lval_T *lp,
+ char_u *name,
+ char_u *key,
+ int len,
+ char_u **key_end,
+ typval_T *var1,
+ int flags,
+ int unlet,
+ typval_T *rettv)
+{
+ int quiet = flags & GLV_QUIET;
+ char_u *p = *key_end;
+
+ if (len == -1)
+ {
+ // "[key]": get key from "var1"
+ key = tv_get_string_chk(var1); // is number or string
+ if (key == NULL)
+ return GLV_FAIL;
+ }
+ lp->ll_list = NULL;
+ lp->ll_object = NULL;
+ lp->ll_class = NULL;
+
+ // a NULL dict is equivalent with an empty dict
+ if (lp->ll_tv->vval.v_dict == NULL)
+ {
+ lp->ll_tv->vval.v_dict = dict_alloc();
+ if (lp->ll_tv->vval.v_dict == NULL)
+ return GLV_FAIL;
+ ++lp->ll_tv->vval.v_dict->dv_refcount;
+ }
+ lp->ll_dict = lp->ll_tv->vval.v_dict;
+
+ lp->ll_di = dict_find(lp->ll_dict, key, len);
+
+ // When assigning to a scope dictionary check that a function and
+ // variable name is valid (only variable name unless it is l: or
+ // g: dictionary). Disallow overwriting a builtin function.
+ if (rettv != NULL && lp->ll_dict->dv_scope != 0)
+ {
+ int prevval;
+
+ if (len != -1)
+ {
+ prevval = key[len];
+ key[len] = NUL;
+ }
+ else
+ prevval = 0; // avoid compiler warning
+ int wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE
+ && (rettv->v_type == VAR_FUNC
+ || rettv->v_type == VAR_PARTIAL)
+ && var_wrong_func_name(key, lp->ll_di == NULL))
+ || !valid_varname(key, -1, TRUE);
+ if (len != -1)
+ key[len] = prevval;
+ if (wrong)
+ return GLV_FAIL;
+ }
+
+ if (lp->ll_valtype != NULL)
+ // use the type of the member
+ lp->ll_valtype = lp->ll_valtype->tt_member;
+
+ if (lp->ll_di == NULL)
+ {
+ // Can't add "v:" or "a:" variable.
+ if (lp->ll_dict == get_vimvar_dict()
+ || &lp->ll_dict->dv_hashtab == get_funccal_args_ht())
+ {
+ semsg(_(e_illegal_variable_name_str), name);
+ return GLV_FAIL;
+ }
+
+ // Key does not exist in dict: may need to add it.
+ if (*p == '[' || *p == '.' || unlet)
+ {
+ if (!quiet)
+ semsg(_(e_key_not_present_in_dictionary_str), key);
+ return GLV_FAIL;
+ }
+ if (len == -1)
+ lp->ll_newkey = vim_strsave(key);
+ else
+ lp->ll_newkey = vim_strnsave(key, len);
+ if (lp->ll_newkey == NULL)
+ p = NULL;
+
+ *key_end = p;
+ return GLV_STOP;
+ }
+ // existing variable, need to check if it can be changed
+ else if ((flags & GLV_READ_ONLY) == 0
+ && (var_check_ro(lp->ll_di->di_flags, name, FALSE)
+ || var_check_lock(lp->ll_di->di_flags, name, FALSE)))
+ return GLV_FAIL;
+
+ lp->ll_tv = &lp->ll_di->di_tv;
+
+ return GLV_OK;
+}
+
+/*
+ * Get an blob lval variable that can be assigned a value to: "name",
+ * "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc.
+ *
+ * 'var1' specifies the starting blob index and 'var2' specifies the ending
+ * blob index. If the first index is not specified in a range, then 'empty1'
+ * is TRUE. If 'quiet' is TRUE, then error messages are not displayed for
+ * invalid indexes.
+ *
+ * The blob is returned in 'lp'. Returns OK on success and FAIL on failure.
+ */
+ static int
+get_lval_blob(
+ lval_T *lp,
+ typval_T *var1,
+ typval_T *var2,
+ int empty1,
+ int quiet)
+{
+ long bloblen = blob_len(lp->ll_tv->vval.v_blob);
+
+ // Get the number and item for the only or first index of the List.
+ if (empty1)
+ lp->ll_n1 = 0;
+ else
+ // is number or string
+ lp->ll_n1 = (long)tv_get_number(var1);
+
+ if (check_blob_index(bloblen, lp->ll_n1, quiet) == FAIL)
+ return FAIL;
+ if (lp->ll_range && !lp->ll_empty2)
+ {
+ lp->ll_n2 = (long)tv_get_number(var2);
+ if (check_blob_range(bloblen, lp->ll_n1, lp->ll_n2, quiet) == FAIL)
+ return FAIL;
+ }
+
+ if (!lp->ll_range)
+ // Indexing a single byte in a blob. So the rhs type is a
+ // number.
+ lp->ll_valtype = &t_number;
+
+ lp->ll_blob = lp->ll_tv->vval.v_blob;
+ lp->ll_tv = NULL;
+
+ return OK;
+}
+
+/*
+ * Get a List lval variable that can be assigned a value to: "name",
+ * "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]", etc.
+ *
+ * 'var1' specifies the starting List index and 'var2' specifies the ending
+ * List index. If the first index is not specified in a range, then 'empty1'
+ * is TRUE. If 'quiet' is TRUE, then error messages are not displayed for
+ * invalid indexes.
+ *
+ * The List is returned in 'lp'. Returns OK on success and FAIL on failure.
+ */
+ static int
+get_lval_list(
+ lval_T *lp,
+ typval_T *var1,
+ typval_T *var2,
+ int empty1,
+ int flags,
+ int quiet)
+{
+ /*
+ * Get the number and item for the only or first index of the List.
+ */
+ if (empty1)
+ lp->ll_n1 = 0;
+ else
+ // is number or string
+ lp->ll_n1 = (long)tv_get_number(var1);
+
+ lp->ll_dict = NULL;
+ lp->ll_object = NULL;
+ lp->ll_class = NULL;
+ lp->ll_list = lp->ll_tv->vval.v_list;
+ lp->ll_li = check_range_index_one(lp->ll_list, &lp->ll_n1,
+ (flags & GLV_ASSIGN_WITH_OP) == 0, quiet);
+ if (lp->ll_li == NULL)
+ return FAIL;
+
+ if (lp->ll_valtype != NULL && !lp->ll_range)
+ // use the type of the member
+ lp->ll_valtype = lp->ll_valtype->tt_member;
+
+ /*
+ * May need to find the item or absolute index for the second
+ * index of a range.
+ * When no index given: "lp->ll_empty2" is TRUE.
+ * Otherwise "lp->ll_n2" is set to the second index.
+ */
+ if (lp->ll_range && !lp->ll_empty2)
+ {
+ lp->ll_n2 = (long)tv_get_number(var2);
+ // is number or string
+ if (check_range_index_two(lp->ll_list,
+ &lp->ll_n1, lp->ll_li, &lp->ll_n2, quiet) == FAIL)
+ return FAIL;
+ }
+
+ lp->ll_tv = &lp->ll_li->li_tv;
+
+ return OK;
+}
+
+/*
+ * Get a class or object lval method in class "cl". The 'key' argument points
+ * to the method name and 'key_end' points to the character after 'key'.
+ * 'v_type' is VAR_CLASS or VAR_OBJECT.
+ *
+ * The method index, method function pointer and method type are returned in
+ * "lp".
+ */
+ static void
+get_lval_oc_method(
+ lval_T *lp,
+ class_T *cl,
+ char_u *key,
+ char_u *key_end,
+ vartype_T v_type)
+{
+ // Look for a method with this name.
+ // round 1: class functions (skipped for an object)
+ // round 2: object methods
+ for (int round = v_type == VAR_OBJECT ? 2 : 1; round <= 2; ++round)
+ {
+ int m_idx;
+ ufunc_T *fp;
+
+ fp = method_lookup(cl, round == 1 ? VAR_CLASS : VAR_OBJECT,
+ key, key_end - key, &m_idx);
+ lp->ll_oi = m_idx;
+ if (fp != NULL)
+ {
+ lp->ll_ufunc = fp;
+ lp->ll_valtype = fp->uf_func_type;
+ break;
+ }
+ }
+}
+
+/*
+ * Get a class or object lval variable in class "cl". The "key" argument
+ * points to the variable name and "key_end" points to the character after
+ * "key". "v_type" is VAR_CLASS or VAR_OBJECT. "cl_exec" is the class that is
+ * executing, or NULL.
+ *
+ * The variable index, typval and type are returned in "lp". Returns FAIL if
+ * the variable is not writable. Otherwise returns OK.
+ */
+ static int
+get_lval_oc_variable(
+ lval_T *lp,
+ class_T *cl,
+ char_u *key,
+ char_u *key_end,
+ vartype_T v_type,
+ class_T *cl_exec,
+ int flags)
+{
+ int m_idx;
+ ocmember_T *om;
+
+ om = member_lookup(cl, v_type, key, key_end - key, &m_idx);
+ lp->ll_oi = m_idx;
+ if (om == NULL)
+ return OK;
+
+ // Check variable is accessible
+ if (get_lval_check_access(cl_exec, cl, om, key_end, flags) == FAIL)
+ return FAIL;
+
+ // When lhs is used to modify the variable, check it is not a read-only
+ // variable.
+ if ((flags & GLV_READ_ONLY) == 0 && (*key_end != '.' && *key_end != '[')
+ && oc_var_check_ro(cl, om))
+ return FAIL;
+
+ lp->ll_valtype = om->ocm_type;
+
+ if (v_type == VAR_OBJECT)
+ lp->ll_tv = ((typval_T *)(lp->ll_tv->vval.v_object + 1)) + m_idx;
+ else
+ lp->ll_tv = &cl->class_members_tv[m_idx];
+
+ return OK;
+}
+
+/*
+ * Get a Class or Object lval variable or method that can be assigned a value
+ * to: "name", "name.key", "name.key[expr]" etc.
+ *
+ * The 'key' argument points to the member name and 'key_end' points to the
+ * character after 'key'. 'v_type' is VAR_CLASS or VAR_OBJECT. 'cl_exec' is
+ * the class that is executing, or NULL. If 'quiet' is TRUE, then error
+ * messages are not displayed for invalid indexes.
+ *
+ * The Class or Object is returned in 'lp'. Returns OK on success and FAIL on
+ * failure.
+ */
+ static int
+get_lval_class_or_obj(
+ lval_T *lp,
+ char_u *key,
+ char_u *key_end,
+ vartype_T v_type,
+ class_T *cl_exec,
+ int flags,
+ int quiet)
+{
+ lp->ll_dict = NULL;
+ lp->ll_list = NULL;
+
+ class_T *cl;
+ if (v_type == VAR_OBJECT)
+ {
+ if (lp->ll_tv->vval.v_object == NULL)
+ {
+ if (!quiet)
+ emsg(_(e_using_null_object));
+ return FAIL;
+ }
+ cl = lp->ll_tv->vval.v_object->obj_class;
+ lp->ll_object = lp->ll_tv->vval.v_object;
+ }
+ else
+ {
+ cl = lp->ll_tv->vval.v_class;
+ lp->ll_object = NULL;
+ }
+ lp->ll_class = cl;
+
+ if (cl == NULL)
+ // TODO: what if class is NULL?
+ return OK;
+
+ lp->ll_valtype = NULL;
+
+ if (flags & GLV_PREFER_FUNC)
+ get_lval_oc_method(lp, cl, key, key_end, v_type);
+
+ // Look for object/class member variable
+ if (lp->ll_valtype == NULL)
+ {
+ if (get_lval_oc_variable(lp, cl, key, key_end, v_type, cl_exec, flags)
+ == FAIL)
+ return FAIL;
+ }
+
+ if (lp->ll_valtype == NULL)
+ {
+ member_not_found_msg(cl, v_type, key, key_end - key);
+ return FAIL;
+ }
+
+ return OK;
+}
+
+/*
+ * Check whether dot (".") is allowed after the variable "name" with type
+ * "v_type". Only Dict, Class and Object types support a dot after the name.
+ * Returns TRUE if dot is allowed after the name.
+ */
+ static int
+dot_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
+{
+ if (v_type != VAR_DICT && v_type != VAR_OBJECT && v_type != VAR_CLASS)
+ {
+ if (!quiet)
+ semsg(_(e_dot_not_allowed_after_str_str),
+ vartype_name(v_type), name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Check whether left bracket ("[") is allowed after the variable "name" with
+ * type "v_type". Only Dict, List and Blob types support a bracket after the
+ * variable name. Returns TRUE if bracket is allowed after the name.
+ */
+ static int
+bracket_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
+{
+ if (v_type == VAR_CLASS || v_type == VAR_OBJECT)
+ {
+ if (!quiet)
+ semsg(_(e_index_not_allowed_after_str_str),
+ vartype_name(v_type), name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Check whether the variable "name" with type "v_type" can be followed by an
+ * index. Only Dict, List, Blob, Object and Class types support indexing.
+ * Returns TRUE if indexing is allowed after the name.
+ */
+ static int
+index_allowed_after_type(char_u *name, vartype_T v_type, int quiet)
+{
+ if (v_type != VAR_LIST && v_type != VAR_DICT && v_type != VAR_BLOB &&
+ v_type != VAR_OBJECT && v_type != VAR_CLASS)
+ {
+ if (!quiet)
+ semsg(_(e_index_not_allowed_after_str_str),
+ vartype_name(v_type), name);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Get the lval of a list/dict/blob/object/class subitem starting at "p". Loop
+ * until no more [idx] or .key is following.
+ *
+ * If "rettv" is not NULL it points to the value to be assigned.
+ * "unlet" is TRUE for ":unlet".
+ *
+ * Returns a pointer to the character after the subscript on success or NULL on
+ * failure.
+ */
+ static char_u *
+get_lval_subscript(
+ lval_T *lp,
+ char_u *p,
+ char_u *name,
+ typval_T *rettv,
+ hashtab_T *ht,
+ dictitem_T *v,
+ int unlet,
+ int flags, // GLV_ values
+ class_T *cl_exec)
+{
+ int vim9script = in_vim9script();
+ int quiet = flags & GLV_QUIET;
+ char_u *key = NULL;
+ int len;
+ typval_T var1;
+ typval_T var2;
+ int empty1 = FALSE;
+ int rc = FAIL;
+
+ /*
+ * Loop until no more [idx] or .key is following.
+ */
+ var1.v_type = VAR_UNKNOWN;
+ var2.v_type = VAR_UNKNOWN;
+
+ while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
+ {
+ vartype_T v_type = lp->ll_tv->v_type;
+
+ if (*p == '.' && !dot_allowed_after_type(name, v_type, quiet))
+ goto done;
+
+ if (*p == '[' && !bracket_allowed_after_type(name, v_type, quiet))
+ goto done;
+
+ if (!index_allowed_after_type(name, v_type, quiet))
+ goto done;
+
+ // A NULL list/blob works like an empty list/blob, allocate one now.
+ int r = OK;
+ if (v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL)
+ r = rettv_list_alloc(lp->ll_tv);
+ else if (v_type == VAR_BLOB && lp->ll_tv->vval.v_blob == NULL)
+ r = rettv_blob_alloc(lp->ll_tv);
+ if (r == FAIL)
+ goto done;
+
+ if (lp->ll_range)
+ {
+ if (!quiet)
+ emsg(_(e_slice_must_come_last));
+ goto done;
+ }
+#ifdef LOG_LOCKVAR
+ ch_log(NULL, "LKVAR: get_lval() loop: p: %s, type: %s", p,
+ vartype_name(v_type));
+#endif
+
+ if (vim9script && lp->ll_valtype == NULL
+ && v != NULL
+ && lp->ll_tv == &v->di_tv
+ && ht != NULL && ht == get_script_local_ht())
+ {
+ svar_T *sv = find_typval_in_script(lp->ll_tv, 0, TRUE);
+
+ // Vim9 script local variable: get the type
+ if (sv != NULL)
+ {
+ lp->ll_valtype = sv->sv_type;
+#ifdef LOG_LOCKVAR
+ ch_log(NULL, "LKVAR: ... loop: vim9 assign type: %s",
+ vartype_name(lp->ll_valtype->tt_type));
+#endif
+ }
+ }
+
+ len = -1;
+ if (*p == '.')
+ {
+ key = p + 1;
+
+ for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len)
+ ;
+ if (len == 0)
+ {
+ if (!quiet)
+ emsg(_(e_cannot_use_empty_key_for_dictionary));
+ goto done;
+ }
+ p = key + len;
+ }
+ else
+ {
+ // Get the index [expr] or the first index [expr: ].
+ p = skipwhite(p + 1);
+ if (*p == ':')
+ empty1 = TRUE;
+ else
+ {
+ empty1 = FALSE;
+ if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) // recursive!
+ goto done;
+ if (tv_get_string_chk(&var1) == NULL)
+ // not a number or string
+ goto done;
+ p = skipwhite(p);
+ }
+
+ // Optionally get the second index [ :expr].
+ if (*p == ':')
+ {
+ if (v_type == VAR_DICT)
+ {
+ if (!quiet)
+ emsg(_(e_cannot_slice_dictionary));
+ goto done;
+ }
+ if (rettv != NULL
+ && !(rettv->v_type == VAR_LIST
+ && rettv->vval.v_list != NULL)
+ && !(rettv->v_type == VAR_BLOB
+ && rettv->vval.v_blob != NULL))
+ {
+ if (!quiet)
+ emsg(_(e_slice_requires_list_or_blob_value));
+ goto done;
+ }
+ p = skipwhite(p + 1);
+ if (*p == ']')
+ lp->ll_empty2 = TRUE;
+ else
+ {
+ lp->ll_empty2 = FALSE;
+ // recursive!
+ if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL)
+ goto done;
+ if (tv_get_string_chk(&var2) == NULL)
+ // not a number or string
+ goto done;
+ }
+ lp->ll_range = TRUE;
+ }
+ else
+ lp->ll_range = FALSE;
+
+ if (*p != ']')
+ {
+ if (!quiet)
+ emsg(_(e_missing_closing_square_brace));
+ goto done;
+ }
+
+ // Skip to past ']'.
+ ++p;
+ }
+#ifdef LOG_LOCKVAR
+ if (len == -1)
+ ch_log(NULL, "LKVAR: ... loop: p: %s, '[' key: %s", p,
+ empty1 ? ":" : (char*)tv_get_string(&var1));
+ else
+ ch_log(NULL, "LKVAR: ... loop: p: %s, '.' key: %s", p, key);
+#endif
+
+ if (v_type == VAR_DICT)
+ {
+ glv_status_T glv_status;
+
+ glv_status = get_lval_dict_item(lp, name, key, len, &p, &var1,
+ flags, unlet, rettv);
+ if (glv_status == GLV_FAIL)
+ goto done;
+ if (glv_status == GLV_STOP)
+ break;
+ }
+ else if (v_type == VAR_BLOB)
+ {
+ if (get_lval_blob(lp, &var1, &var2, empty1, quiet) == FAIL)
+ goto done;
+
+ break;
+ }
+ else if (v_type == VAR_LIST)
+ {
+ if (get_lval_list(lp, &var1, &var2, empty1, flags, quiet) == FAIL)
+ goto done;
+ }
+ else // v_type == VAR_CLASS || v_type == VAR_OBJECT
+ {
+ if (get_lval_class_or_obj(lp, key, p, v_type, cl_exec, flags,
+ quiet) == FAIL)
+ goto done;
+ }
+
+ clear_tv(&var1);
+ clear_tv(&var2);
+ var1.v_type = VAR_UNKNOWN;
+ var2.v_type = VAR_UNKNOWN;
+ }
+
+ rc = OK;
+
+done:
+ clear_tv(&var1);
+ clear_tv(&var2);
+ return rc == OK ? p : NULL;
+}
+
/*
* Get an lval: variable, Dict item or List item that can be assigned a value
* to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]",
@@ -1274,11 +1975,6 @@ get_lval(
char_u *expr_start, *expr_end;
int cc;
dictitem_T *v = NULL;
- typval_T var1;
- typval_T var2;
- int empty1 = FALSE;
- char_u *key = NULL;
- int len;
hashtab_T *ht = NULL;
int quiet = flags & GLV_QUIET;
int writing = 0;
@@ -1410,8 +2106,7 @@ get_lval(
if (import != NULL)
{
p++; // skip '.'
- p = get_lval_imported(lp, rettv, import->imp_sid, p, &v,
- fne_flags, vim9script);
+ p = get_lval_imported(lp, import->imp_sid, p, &v, fne_flags);
if (p == NULL)
return NULL;
}
@@ -1456,447 +2151,21 @@ get_lval(
return NULL;
}
- /*
- * Loop until no more [idx] or .key is following.
- */
- var1.v_type = VAR_UNKNOWN;
- var2.v_type = VAR_UNKNOWN;
- while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
- {
- vartype_T v_type = lp->ll_tv->v_type;
-
- if (*p == '.' && v_type != VAR_DICT
- && v_type != VAR_OBJECT
- && v_type != VAR_CLASS)
- {
- if (!quiet)
- semsg(_(e_dot_not_allowed_after_str_str),
- vartype_name(v_type), name);
- return NULL;
- }
- if (v_type != VAR_LIST
- && v_type != VAR_DICT
- && v_type != VAR_BLOB
- && v_type != VAR_OBJECT
- && v_type != VAR_CLASS)
- {
- if (!quiet)
- semsg(_(e_index_not_allowed_after_str_str),
- vartype_name(v_type), name);
- return NULL;
- }
+ // If the next character is a "." or a "[", then process the subitem.
+ p = get_lval_subscript(lp, p, name, rettv, ht, v, unlet, flags, cl_exec);
+ if (p == NULL)
+ return NULL;
- // A NULL list/blob works like an empty list/blob, allocate one now.
- int r = OK;
- if (v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL)
- r = rettv_list_alloc(lp->ll_tv);
- else if (v_type == VAR_BLOB && lp->ll_tv->vval.v_blob == NULL)
- r = rettv_blob_alloc(lp->ll_tv);
- if (r == FAIL)
- return NULL;
+ if (vim9script && lp->ll_valtype != NULL && rettv != NULL)
+ {
+ where_T where = WHERE_INIT;
- if (lp->ll_range)
- {
- if (!quiet)
- emsg(_(e_slice_must_come_last));
+ // In a vim9 script, do type check and make sure the variable is
+ // writable.
+ if (check_typval_type(lp->ll_valtype, rettv, where) == FAIL)
return NULL;
- }
-#ifdef LOG_LOCKVAR
- ch_log(NULL, "LKVAR: get_lval() loop: p: %s, type: %s", p,
- vartype_name(v_type));
-#endif
-
- if (vim9script && lp->ll_valtype == NULL
- && v != NULL
- && lp->ll_tv == &v->di_tv
- && ht != NULL && ht == get_script_local_ht())
- {
- svar_T *sv = find_typval_in_script(lp->ll_tv, 0, TRUE);
-
- // Vim9 script local variable: get the type
- if (sv != NULL)
- {
- lp->ll_valtype = sv->sv_type;
-#ifdef LOG_LOCKVAR
- ch_log(NULL, "LKVAR: ... loop: vim9 assign type: %s",
- vartype_name(lp->ll_valtype->tt_type));
-#endif
- }
- }
-
- len = -1;
- if (*p == '.')
- {
- key = p + 1;
- for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len)
- ;
- if (len == 0)
- {
- if (!quiet)
- emsg(_(e_cannot_use_empty_key_for_dictionary));
- return NULL;
- }
- p = key + len;
- }
- else
- {
- // Get the index [expr] or the first index [expr: ].
- p = skipwhite(p + 1);
- if (*p == ':')
- empty1 = TRUE;
- else
- {
- empty1 = FALSE;
- if (eval1(&p, &var1, &EVALARG_EVALUATE) == FAIL) // recursive!
- return NULL;
- if (tv_get_string_chk(&var1) == NULL)
- {
- // not a number or string
- clear_tv(&var1);
- return NULL;
- }
- p = skipwhite(p);
- }
-
- // Optionally get the second index [ :expr].
- if (*p == ':')
- {
- if (v_type == VAR_DICT)
- {
- if (!quiet)
- emsg(_(e_cannot_slice_dictionary));
- clear_tv(&var1);
- return NULL;
- }
- if (rettv != NULL
- && !(rettv->v_type == VAR_LIST
- && rettv->vval.v_list != NULL)
- && !(rettv->v_type == VAR_BLOB
- && rettv->vval.v_blob != NULL))
- {
- if (!quiet)
- emsg(_(e_slice_requires_list_or_blob_value));
- clear_tv(&var1);
- return NULL;
- }
- p = skipwhite(p + 1);
- if (*p == ']')
- lp->ll_empty2 = TRUE;
- else
- {
- lp->ll_empty2 = FALSE;
- // recursive!
- if (eval1(&p, &var2, &EVALARG_EVALUATE) == FAIL)
- {
- clear_tv(&var1);
- return NULL;
- }
- if (tv_get_string_chk(&var2) == NULL)
- {
- // not a number or string
- clear_tv(&var1);
- clear_tv(&var2);
- return NULL;
- }
- }
- lp->ll_range = TRUE;
- }
- else
- lp->ll_range = FALSE;
-
- if (*p != ']')
- {
- if (!quiet)
- emsg(_(e_missing_closing_square_brace));
- clear_tv(&var1);
- clear_tv(&var2);
- return NULL;
- }
-
- // Skip to past ']'.
- ++p;
- }
-#ifdef LOG_LOCKVAR
- if (len == -1)
- ch_log(NULL, "LKVAR: ... loop: p: %s, '[' key: %s", p,
- empty1 ? ":" : (char*)tv_get_string(&var1));
- else
- ch_log(NULL, "LKVAR: ... loop: p: %s, '.' key: %s", p, key);
-#endif
-
- if (v_type == VAR_DICT)
- {
- if (len == -1)
- {
- // "[key]": get key from "var1"
- key = tv_get_string_chk(&var1); // is number or string
- if (key == NULL)
- {
- clear_tv(&var1);
- return NULL;
- }
- }
- lp->ll_list = NULL;
- lp->ll_object = NULL;
- lp->ll_class = NULL;
-
- // a NULL dict is equivalent with an empty dict
- if (lp->ll_tv->vval.v_dict == NULL)
- {
- lp->ll_tv->vval.v_dict = dict_alloc();
- if (lp->ll_tv->vval.v_dict == NULL)
- {
- clear_tv(&var1);
- return NULL;
- }
- ++lp->ll_tv->vval.v_dict->dv_refcount;
- }
- lp->ll_dict = lp->ll_tv->vval.v_dict;
-
- lp->ll_di = dict_find(lp->ll_dict, key, len);
-
- // When assigning to a scope dictionary check that a function and
- // variable name is valid (only variable name unless it is l: or
- // g: dictionary). Disallow overwriting a builtin function.
- if (rettv != NULL && lp->ll_dict->dv_scope != 0)
- {
- int prevval;
-
- if (len != -1)
- {
- prevval = key[len];
- key[len] = NUL;
- }
- else
- prevval = 0; // avoid compiler warning
- int wrong = (lp->ll_dict->dv_scope == VAR_DEF_SCOPE
- && (rettv->v_type == VAR_FUNC
- || rettv->v_type == VAR_PARTIAL)
- && var_wrong_func_name(key, lp->ll_di == NULL))
- || !valid_varname(key, -1, TRUE);
- if (len != -1)
- key[len] = prevval;
- if (wrong)
- {
- clear_tv(&var1);
- return NULL;
- }
- }
-
- if (lp->ll_valtype != NULL)
- // use the type of the member
- lp->ll_valtype = lp->ll_valtype->tt_member;
-
- if (lp->ll_di == NULL)
- {
- // Can't add "v:" or "a:" variable.
- if (lp->ll_dict == get_vimvar_dict()
- || &lp->ll_dict->dv_hashtab == get_funccal_args_ht())
- {
- semsg(_(e_illegal_variable_name_str), name);
- clear_tv(&var1);
- return NULL;
- }
-
- // Key does not exist in dict: may need to add it.
- if (*p == '[' || *p == '.' || unlet)
- {
- if (!quiet)
- semsg(_(e_key_not_present_in_dictionary_str), key);
- clear_tv(&var1);
- return NULL;
- }
- if (len == -1)
- lp->ll_newkey = vim_strsave(key);
- else
- lp->ll_newkey = vim_strnsave(key, len);
- clear_tv(&var1);
- if (lp->ll_newkey == NULL)
- p = NULL;
- break;
- }
- // existing variable, need to check if it can be changed
- else if ((flags & GLV_READ_ONLY) == 0
- && (var_check_ro(lp->ll_di->di_flags, name, FALSE)
- || var_check_lock(lp->ll_di->di_flags, name, FALSE)))
- {
- clear_tv(&var1);
- return NULL;
- }
-
- clear_tv(&var1);
- lp->ll_tv = &lp->ll_di->di_tv;
- }
- else if (v_type == VAR_BLOB)
- {
- long bloblen = blob_len(lp->ll_tv->vval.v_blob);
-
- /*
- * Get the number and item for the only or first index of the List.
- */
- if (empty1)
- lp->ll_n1 = 0;
- else
- // is number or string
- lp->ll_n1 = (long)tv_get_number(&var1);
- clear_tv(&var1);
-
- if (check_blob_index(bloblen, lp->ll_n1, quiet) == FAIL)
- {
- clear_tv(&var2);
- return NULL;
- }
- if (lp->ll_range && !lp->ll_empty2)
- {
- lp->ll_n2 = (long)tv_get_number(&var2);
- clear_tv(&var2);
- if (check_blob_range(bloblen, lp->ll_n1, lp->ll_n2, quiet)
- == FAIL)
- return NULL;
- }
- lp->ll_blob = lp->ll_tv->vval.v_blob;
- lp->ll_tv = NULL;
- break;
- }
- else if (v_type == VAR_LIST)
- {
- /*
- * Get the number and item for the only or first index of the List.
- */
- if (empty1)
- lp->ll_n1 = 0;
- else
- // is number or string
- lp->ll_n1 = (long)tv_get_number(&var1);
- clear_tv(&var1);
-
- lp->ll_dict = NULL;
- lp->ll_object = NULL;
- lp->ll_class = NULL;
- lp->ll_list = lp->ll_tv->vval.v_list;
- lp->ll_li = check_range_index_one(lp->ll_list, &lp->ll_n1,
- (flags & GLV_ASSIGN_WITH_OP) == 0, quiet);
- if (lp->ll_li == NULL)
- {
- clear_tv(&var2);
- return NULL;
- }
-
- if (lp->ll_valtype != NULL)
- // use the type of the member
- lp->ll_valtype = lp->ll_valtype->tt_member;
-
- /*
- * May need to find the item or absolute index for the second
- * index of a range.
- * When no index given: "lp->ll_empty2" is TRUE.
- * Otherwise "lp->ll_n2" is set to the second index.
- */
- if (lp->ll_range && !lp->ll_empty2)
- {
- lp->ll_n2 = (long)tv_get_number(&var2);
- // is number or string
- clear_tv(&var2);
- if (check_range_index_two(lp->ll_list,
- &lp->ll_n1, lp->ll_li,
- &lp->ll_n2, quiet) == FAIL)
- return NULL;
- }
-
- lp->ll_tv = &lp->ll_li->li_tv;
- }
- else // v_type == VAR_CLASS || v_type == VAR_OBJECT
- {
- lp->ll_dict = NULL;
- lp->ll_list = NULL;
-
- class_T *cl;
- if (v_type == VAR_OBJECT)
- {
- if (lp->ll_tv->vval.v_object == NULL)
- {
- if (!quiet)
- emsg(_(e_using_null_object));
- return NULL;
- }
- cl = lp->ll_tv->vval.v_object->obj_class;
- lp->ll_object = lp->ll_tv->vval.v_object;
- }
- else
- {
- cl = lp->ll_tv->vval.v_class;
- lp->ll_object = NULL;
- }
- lp->ll_class = cl;
-
- // TODO: what if class is NULL?
- if (cl != NULL)
- {
- lp->ll_valtype = NULL;
-
- if (flags & GLV_PREFER_FUNC)
- {
- // First look for a function with this name.
- // round 1: class functions (skipped for an object)
- // round 2: object methods
- for (int round = v_type == VAR_OBJECT ? 2 : 1;
- round <= 2; ++round)
- {
- int m_idx;
- ufunc_T *fp;
-
- fp = method_lookup(cl,
- round == 1 ? VAR_CLASS : VAR_OBJECT,
- key, p - key, &m_idx);
- lp->ll_oi = m_idx;
- if (fp != NULL)
- {
- lp->ll_ufunc = fp;
- lp->ll_valtype = fp->uf_func_type;
- break;
- }
- }
- }
-
- if (lp->ll_valtype == NULL)
- {
- int m_idx;
- ocmember_T *om
- = member_lookup(cl, v_type, key, p - key, &m_idx);
- lp->ll_oi = m_idx;
- if (om != NULL)
- {
- if (get_lval_check_access(cl_exec, cl, om,
- p, flags) == FAIL)
- return NULL;
-
- // When lhs is used to modify the variable, check it is
- // not a read-only variable.
- if ((flags & GLV_READ_ONLY) == 0
- && (*p != '.' && *p != '[')
- && oc_var_check_ro(cl, om))
- return NULL;
-
- lp->ll_valtype = om->ocm_type;
-
- if (v_type == VAR_OBJECT)
- lp->ll_tv = ((typval_T *)(
- lp->ll_tv->vval.v_object + 1)) + m_idx;
- else
- lp->ll_tv = &cl->class_members_tv[m_idx];
- }
- }
-
- if (lp->ll_valtype == NULL)
- {
- member_not_found_msg(cl, v_type, key, p - key);
- return NULL;
- }
- }
- }
}
- clear_tv(&var1);
lp->ll_name_end = p;
return p;
}
@@ -2085,159 +2354,238 @@ set_var_lval(
}
/*
- * Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2"
- * and "tv1 .= tv2"
+ * Handle "blob1 += blob2".
* Returns OK or FAIL.
*/
- int
-tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
+ static int
+tv_op_blob(typval_T *tv1, typval_T *tv2, char_u *op)
+{
+ if (*op != '+' || tv2->v_type != VAR_BLOB)
+ return FAIL;
+
+ // Blob += Blob
+ if (tv2->vval.v_blob == NULL)
+ return OK;
+
+ if (tv1->vval.v_blob == NULL)
+ {
+ tv1->vval.v_blob = tv2->vval.v_blob;
+ ++tv1->vval.v_blob->bv_refcount;
+ return OK;
+ }
+
+ blob_T *b1 = tv1->vval.v_blob;
+ blob_T *b2 = tv2->vval.v_blob;
+ int len = blob_len(b2);
+
+ for (int i = 0; i < len; i++)
+ ga_append(&b1->bv_ga, blob_get(b2, i));
+
+ return OK;
+}
+
+/*
+ * Handle "list1 += list2".
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op_list(typval_T *tv1, typval_T *tv2, char_u *op)
+{
+ if (*op != '+' || tv2->v_type != VAR_LIST)
+ return FAIL;
+
+ // List += List
+ if (tv2->vval.v_list == NULL)
+ return OK;
+
+ if (tv1->vval.v_list == NULL)
+ {
+ tv1->vval.v_list = tv2->vval.v_list;
+ ++tv1->vval.v_list->lv_refcount;
+ }
+ else
+ list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
+
+ return OK;
+}
+
+/*
+ * Handle number operations:
+ * nr += nr , nr -= nr , nr *=nr , nr /= nr , nr %= nr
+ *
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op_number(typval_T *tv1, typval_T *tv2, char_u *op)
{
varnumber_T n;
+ int failed = FALSE;
+
+ n = tv_get_number(tv1);
+ if (tv2->v_type == VAR_FLOAT)
+ {
+ float_T f = n;
+
+ if (*op == '%')
+ return FAIL;
+ switch (*op)
+ {
+ case '+': f += tv2->vval.v_float; break;
+ case '-': f -= tv2->vval.v_float; break;
+ case '*': f *= tv2->vval.v_float; break;
+ case '/': f /= tv2->vval.v_float; break;
+ }
+ clear_tv(tv1);
+ tv1->v_type = VAR_FLOAT;
+ tv1->vval.v_float = f;
+ }
+ else
+ {
+ switch (*op)
+ {
+ case '+': n += tv_get_number(tv2); break;
+ case '-': n -= tv_get_number(tv2); break;
+ case '*': n *= tv_get_number(tv2); break;
+ case '/': n = num_divide(n, tv_get_number(tv2), &failed); break;
+ case '%': n = num_modulus(n, tv_get_number(tv2), &failed); break;
+ }
+ clear_tv(tv1);
+ tv1->v_type = VAR_NUMBER;
+ tv1->vval.v_number = n;
+ }
+
+ return failed ? FAIL : OK;
+}
+
+/*
+ * Handle "str1 .= str2"
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op_string(typval_T *tv1, typval_T *tv2, char_u *op UNUSED)
+{
char_u numbuf[NUMBUFLEN];
char_u *s;
- int failed = FALSE;
- // Can't do anything with a Funcref or Dict or Type on the right.
+ if (tv2->v_type == VAR_FLOAT)
+ return FAIL;
+
+ // str .= str
+ s = tv_get_string(tv1);
+ s = concat_str(s, tv_get_string_buf(tv2, numbuf));
+ clear_tv(tv1);
+ tv1->v_type = VAR_STRING;
+ tv1->vval.v_string = s;
+
+ return OK;
+}
+
+/*
+ * Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2"
+ * and "tv1 .= tv2"
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op_nr_or_string(typval_T *tv1, typval_T *tv2, char_u *op)
+{
+ if (tv2->v_type == VAR_LIST)
+ return FAIL;
+
+ if (vim_strchr((char_u *)"+-*/%", *op) != NULL)
+ return tv_op_number(tv1, tv2, op);
+
+ return tv_op_string(tv1, tv2, op);
+}
+
+/*
+ * Handle "f1 += f2", "f1 -= f2", "f1 *= f2", "f1 /= f2".
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op_float(typval_T *tv1, typval_T *tv2, char_u *op)
+{
+ float_T f;
+
+ if (*op == '%' || *op == '.'
+ || (tv2->v_type != VAR_FLOAT
+ && tv2->v_type != VAR_NUMBER
+ && tv2->v_type != VAR_STRING))
+ return FAIL;
+
+ if (tv2->v_type == VAR_FLOAT)
+ f = tv2->vval.v_float;
+ else
+ f = tv_get_number(tv2);
+ switch (*op)
+ {
+ case '+': tv1->vval.v_float += f; break;
+ case '-': tv1->vval.v_float -= f; break;
+ case '*': tv1->vval.v_float *= f; break;
+ case '/': tv1->vval.v_float /= f; break;
+ }
+
+ return OK;
+}
+
+/*
+ * Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2"
+ * and "tv1 .= tv2"
+ * Returns OK or FAIL.
+ */
+ int
+tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
+{
+ // Can't do anything with a Funcref or Dict on the right.
// v:true and friends only work with "..=".
- if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT
- && tv2->v_type != VAR_CLASS && tv2->v_type != VAR_TYPEALIAS
- && ((tv2->v_type != VAR_BOOL && tv2->v_type != VAR_SPECIAL)
- || *op == '.'))
- {
- switch (tv1->v_type)
- {
- case VAR_UNKNOWN:
- case VAR_ANY:
- case VAR_VOID:
- case VAR_DICT:
- case VAR_FUNC:
- case VAR_PARTIAL:
- case VAR_BOOL:
- case VAR_SPECIAL:
- case VAR_JOB:
- case VAR_CHANNEL:
- case VAR_INSTR:
- case VAR_OBJECT:
- break;
- case VAR_CLASS:
- case VAR_TYPEALIAS:
- check_typval_is_value(tv1);
- return FAIL;
+ if (tv2->v_type == VAR_FUNC || tv2->v_type == VAR_DICT
+ || ((tv2->v_type == VAR_BOOL || tv2->v_type == VAR_SPECIAL)
+ && *op != '.'))
+ {
+ semsg(_(e_wrong_variable_type_for_str_equal), op);
+ return FAIL;
+ }
- case VAR_BLOB:
- if (*op != '+' || tv2->v_type != VAR_BLOB)
- break;
- // BLOB += BLOB
- if (tv1->vval.v_blob != NULL && tv2->vval.v_blob != NULL)
- {
- blob_T *b1 = tv1->vval.v_blob;
- blob_T *b2 = tv2->vval.v_blob;
- int i, len = blob_len(b2);
- for (i = 0; i < len; i++)
- ga_append(&b1->bv_ga, blob_get(b2, i));
- }
- return OK;
+ int retval = FAIL;
+ switch (tv1->v_type)
+ {
+ case VAR_UNKNOWN:
+ case VAR_ANY:
+ case VAR_VOID:
+ case VAR_DICT:
+ case VAR_FUNC:
+ case VAR_PARTIAL:
+ case VAR_BOOL:
+ case VAR_SPECIAL:
+ case VAR_JOB:
+ case VAR_CHANNEL:
+ case VAR_INSTR:
+ case VAR_OBJECT:
+ case VAR_CLASS:
+ case VAR_TYPEALIAS:
+ break;
- case VAR_LIST:
- if (*op != '+' || tv2->v_type != VAR_LIST)
- break;
- // List += List
- if (tv2->vval.v_list != NULL)
- {
- if (tv1->vval.v_list == NULL)
- {
- tv1->vval.v_list = tv2->vval.v_list;
- ++tv1->vval.v_list->lv_refcount;
- }
- else
- list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
- }
- return OK;
+ case VAR_BLOB:
+ retval = tv_op_blob(tv1, tv2, op);
+ break;
- case VAR_NUMBER:
- case VAR_STRING:
- if (tv2->v_type == VAR_LIST)
- break;
- if (vim_strchr((char_u *)"+-*/%", *op) != NULL)
- {
- // nr += nr , nr -= nr , nr *=nr , nr /= nr , nr %= nr
- n = tv_get_number(tv1);
- if (tv2->v_type == VAR_FLOAT)
- {
- float_T f = n;
-
- if (*op == '%')
- break;
- switch (*op)
- {
- case '+': f += tv2->vval.v_float; break;
- case '-': f -= tv2->vval.v_float; break;
- case '*': f *= tv2->vval.v_float; break;
- case '/': f /= tv2->vval.v_float; break;
- }
- clear_tv(tv1);
- tv1->v_type = VAR_FLOAT;
- tv1->vval.v_float = f;
- }
- else
- {
- switch (*op)
- {
- case '+': n += tv_get_number(tv2); break;
- case '-': n -= tv_get_number(tv2); break;
- case '*': n *= tv_get_number(tv2); break;
- case '/': n = num_divide(n, tv_get_number(tv2),
- &failed); break;
- case '%': n = num_modulus(n, tv_get_number(tv2),
- &failed); break;
- }
- clear_tv(tv1);
- tv1->v_type = VAR_NUMBER;
- tv1->vval.v_number = n;
- }
- }
- else
- {
- if (tv2->v_type == VAR_FLOAT)
- break;
-
- // str .= str
- s = tv_get_string(tv1);
- s = concat_str(s, tv_get_string_buf(tv2, numbuf));
- clear_tv(tv1);
- tv1->v_type = VAR_STRING;
- tv1->vval.v_string = s;
- }
- return failed ? FAIL : OK;
+ case VAR_LIST:
+ retval = tv_op_list(tv1, tv2, op);
+ break;
- case VAR_FLOAT:
- {
- float_T f;
-
- if (*op == '%' || *op == '.'
- || (tv2->v_type != VAR_FLOAT
- && tv2->v_type != VAR_NUMBER
- && tv2->v_type != VAR_STRING))
- break;
- if (tv2->v_type == VAR_FLOAT)
- f = tv2->vval.v_float;
- else
- f = tv_get_number(tv2);
- switch (*op)
- {
- case '+': tv1->vval.v_float += f; break;
- case '-': tv1->vval.v_float -= f; break;
- case '*': tv1->vval.v_float *= f; break;
- case '/': tv1->vval.v_float /= f; break;
- }
- }
- return OK;
- }
+ case VAR_NUMBER:
+ case VAR_STRING:
+ retval = tv_op_nr_or_string(tv1, tv2, op);
+ break;
+
+ case VAR_FLOAT:
+ retval = tv_op_float(tv1, tv2, op);
+ break;
}
- if (check_typval_is_value(tv2) == OK)
+ if (retval != OK)
semsg(_(e_wrong_variable_type_for_str_equal), op);
- return FAIL;
+
+ return retval;
}
/*
@@ -2714,7 +3062,7 @@ newline_skip_comments(char_u *arg)
char_u *nl = vim_strchr(p, NL);
if (nl == NULL)
- break;
+ break;
p = nl;
}
if (*p != NL)
@@ -3599,6 +3947,40 @@ eval_addlist(typval_T *tv1, typval_T *tv2)
}
/*
+ * Left or right shift the number "tv1" by the number "tv2" and store the
+ * result in "tv1".
+ *
+ * Return OK or FAIL.
+ */
+ static int
+eval_shift_number(typval_T *tv1, typval_T *tv2, int shift_type)
+{
+ if (tv2->v_type != VAR_NUMBER || tv2->vval.v_number < 0)
+ {
+ // right operand should be a positive number
+ if (tv2->v_type != VAR_NUMBER)
+ emsg(_(e_bitshift_ops_must_be_number));
+ else
+ emsg(_(e_bitshift_ops_must_be_positive));
+ clear_tv(tv1);
+ clear_tv(tv2);
+ return FAIL;
+ }
+
+ if (tv2->vval.v_number > MAX_LSHIFT_BITS)
+ // shifting more bits than we have always results in zero
+ tv1->vval.v_number = 0;
+ else if (shift_type == EXPR_LSHIFT)
+ tv1->vval.v_number =
+ (uvarnumber_T)tv1->vval.v_number << tv2->vval.v_number;
+ else
+ tv1->vval.v_number =
+ (uvarnumber_T)tv1->vval.v_number >> tv2->vval.v_number;
+
+ return OK;
+}
+
+/*
* Handle the bitwise left/right shift operator expression:
* var1 << var2
* var1 >> var2
@@ -3624,16 +4006,16 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
{
char_u *p;
int getnext;
- exprtype_T type;
+ exprtype_T exprtype;
int evaluate;
typval_T var2;
int vim9script;
p = eval_next_non_blank(*arg, evalarg, &getnext);
if (p[0] == '<' && p[1] == '<')
- type = EXPR_LSHIFT;
+ exprtype = EXPR_LSHIFT;
else if (p[0] == '>' && p[1] == '>')
- type = EXPR_RSHIFT;
+ exprtype = EXPR_RSHIFT;
else
return OK;
@@ -3678,27 +4060,8 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
if (evaluate)
{
- if (var2.v_type != VAR_NUMBER || var2.vval.v_number < 0)
- {
- // right operand should be a positive number
- if (var2.v_type != VAR_NUMBER)
- emsg(_(e_bitshift_ops_must_be_number));
- else
- emsg(_(e_bitshift_ops_must_be_positive));
- clear_tv(rettv);
- clear_tv(&var2);
+ if (eval_shift_number(rettv, &var2, exprtype) == FAIL)
return FAIL;
- }
-
- if (var2.vval.v_number > MAX_LSHIFT_BITS)
- // shifting more bits than we have always results in zero
- rettv->vval.v_number = 0;
- else if (type == EXPR_LSHIFT)
- rettv->vval.v_number =
- (uvarnumber_T)rettv->vval.v_number << var2.vval.v_number;
- else
- rettv->vval.v_number =
- (uvarnumber_T)rettv->vval.v_number >> var2.vval.v_number;
}
clear_tv(&var2);
@@ -3708,6 +4071,120 @@ eval5(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
}
/*
+ * Concatenate strings "tv1" and "tv2" and store the result in "tv1".
+ */
+ static int
+eval_concat_str(typval_T *tv1, typval_T *tv2)
+{
+ char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
+ char_u *s1 = tv_get_string_buf(tv1, buf1);
+ char_u *s2 = NULL;
+ char_u *p;
+ int vim9script = in_vim9script();
+
+ if (vim9script && (tv2->v_type == VAR_VOID
+ || tv2->v_type == VAR_CHANNEL
+ || tv2->v_type == VAR_JOB))
+ semsg(_(e_using_invalid_value_as_string_str),
+ vartype_name(tv2->v_type));
+ else if (vim9script && tv2->v_type == VAR_FLOAT)
+ {
+ vim_snprintf((char *)buf2, NUMBUFLEN, "%g",
+ tv2->vval.v_float);
+ s2 = buf2;
+ }
+ else
+ s2 = tv_get_string_buf_chk(tv2, buf2);
+ if (s2 == NULL) // type error ?
+ {
+ clear_tv(tv1);
+ clear_tv(tv2);
+ return FAIL;
+ }
+
+ p = concat_str(s1, s2);
+ clear_tv(tv1);
+ tv1->v_type = VAR_STRING;
+ tv1->vval.v_string = p;
+
+ return OK;
+}
+
+/*
+ * Add or subtract numbers "tv1" and "tv2" and store the result in "tv1".
+ * The numbers can be whole numbers or floats.
+ */
+ static int
+eval_addsub_number(typval_T *tv1, typval_T *tv2, int op)
+{
+ int error = FALSE;
+ varnumber_T n1, n2;
+ float_T f1 = 0, f2 = 0;
+
+ if (tv1->v_type == VAR_FLOAT)
+ {
+ f1 = tv1->vval.v_float;
+ n1 = 0;
+ }
+ else
+ {
+ n1 = tv_get_number_chk(tv1, &error);
+ if (error)
+ {
+ // This can only happen for "list + non-list" or
+ // "blob + non-blob". For "non-list + ..." or
+ // "something - ...", we returned before evaluating the
+ // 2nd operand.
+ clear_tv(tv1);
+ clear_tv(tv2);
+ return FAIL;
+ }
+ if (tv2->v_type == VAR_FLOAT)
+ f1 = n1;
+ }
+ if (tv2->v_type == VAR_FLOAT)
+ {
+ f2 = tv2->vval.v_float;
+ n2 = 0;
+ }
+ else
+ {
+ n2 = tv_get_number_chk(tv2, &error);
+ if (error)
+ {
+ clear_tv(tv1);
+ clear_tv(tv2);
+ return FAIL;
+ }
+ if (tv1->v_type == VAR_FLOAT)
+ f2 = n2;
+ }
+ clear_tv(tv1);
+
+ // If there is a float on either side the result is a float.
+ if (tv1->v_type == VAR_FLOAT || tv2->v_type == VAR_FLOAT)
+ {
+ if (op == '+')
+ f1 = f1 + f2;
+ else
+ f1 = f1 - f2;
+ tv1->v_type = VAR_FLOAT;
+ tv1->vval.v_float = f1;
+ }
+ else
+ {
+ if (op == '+')
+ n1 = n1 + n2;
+ else
+ n1 = n1 - n2;
+ tv1->v_type = VAR_NUMBER;
+ tv1->vval.v_number = n1;
+ }
+
+ return OK;
+}
+
+/*
* Handle fifth level expression:
* + number addition, concatenation of list or blob
* - number subtraction
@@ -3814,33 +4291,8 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
*/
if (op == '.')
{
- char_u buf1[NUMBUFLEN], buf2[NUMBUFLEN];
- char_u *s1 = tv_get_string_buf(rettv, buf1);
- char_u *s2 = NULL;
-
- if (vim9script && (var2.v_type == VAR_VOID
- || var2.v_type == VAR_CHANNEL
- || var2.v_type == VAR_JOB))
- semsg(_(e_using_invalid_value_as_string_str),
- vartype_name(var2.v_type));
- else if (vim9script && var2.v_type == VAR_FLOAT)
- {
- vim_snprintf((char *)buf2, NUMBUFLEN, "%g",
- var2.vval.v_float);
- s2 = buf2;
- }
- else
- s2 = tv_get_string_buf_chk(&var2, buf2);
- if (s2 == NULL) // type error ?
- {
- clear_tv(rettv);
- clear_tv(&var2);
+ if (eval_concat_str(rettv, &var2) == FAIL)
return FAIL;
- }
- p = concat_str(s1, s2);
- clear_tv(rettv);
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = p;
}
else if (op == '+' && rettv->v_type == VAR_BLOB
&& var2.v_type == VAR_BLOB)
@@ -3853,73 +4305,119 @@ eval6(char_u **arg, typval_T *rettv, evalarg_T *evalarg)
}
else
{
- int error = FALSE;
- varnumber_T n1, n2;
- float_T f1 = 0, f2 = 0;
+ if (eval_addsub_number(rettv, &var2, op) == FAIL)
+ return FAIL;
+ }
+ clear_tv(&var2);
+ }
+ }
+ return OK;
+}
- if (rettv->v_type == VAR_FLOAT)
- {
- f1 = rettv->vval.v_float;
- n1 = 0;
- }
- else
- {
- n1 = tv_get_number_chk(rettv, &error);
- if (error)
- {
- // This can only happen for "list + non-list" or
- // "blob + non-blob". For "non-list + ..." or
- // "something - ...", we returned before evaluating the
- // 2nd operand.
- clear_tv(rettv);
- clear_tv(&var2);
- return FAIL;
- }
- if (var2.v_type == VAR_FLOAT)
- f1 = n1;
- }
- if (var2.v_type == VAR_FLOAT)
- {
- f2 = var2.vval.v_float;
- n2 = 0;
- }
- else
- {
- n2 = tv_get_number_chk(&var2, &error);
- if (error)
- {
- clear_tv(rettv);
- clear_tv(&var2);
- return FAIL;
- }
- if (rettv->v_type == VAR_FLOAT)
- f2 = n2;
- }
- clear_tv(rettv);
+/*
+ * Multiply or divide or compute the modulo of numbers "tv1" and "tv2" and
+ * store the result in "tv1". The numbers can be whole numbers or floats.
+ */
+ static int
+eval_multdiv_number(typval_T *tv1, typval_T *tv2, int op)
+{
+ varnumber_T n1, n2;
+ float_T f1, f2;
+ int error;
+ int use_float = FALSE;
- // If there is a float on either side the result is a float.
- if (rettv->v_type == VAR_FLOAT || var2.v_type == VAR_FLOAT)
- {
- if (op == '+')
- f1 = f1 + f2;
- else
- f1 = f1 - f2;
- rettv->v_type = VAR_FLOAT;
- rettv->vval.v_float = f1;
- }
+ f1 = 0;
+ f2 = 0;
+ error = FALSE;
+ if (tv1->v_type == VAR_FLOAT)
+ {
+ f1 = tv1->vval.v_float;
+ use_float = TRUE;
+ n1 = 0;
+ }
+ else
+ n1 = tv_get_number_chk(tv1, &error);
+ clear_tv(tv1);
+ if (error)
+ {
+ clear_tv(tv2);
+ return FAIL;
+ }
+
+ if (tv2->v_type == VAR_FLOAT)
+ {
+ if (!use_float)
+ {
+ f1 = n1;
+ use_float = TRUE;
+ }
+ f2 = tv2->vval.v_float;
+ n2 = 0;
+ }
+ else
+ {
+ n2 = tv_get_number_chk(tv2, &error);
+ clear_tv(tv2);
+ if (error)
+ return FAIL;
+ if (use_float)
+ f2 = n2;
+ }
+
+ /*
+ * Compute the result.
+ * When either side is a float the result is a float.
+ */
+ if (use_float)
+ {
+ if (op == '*')
+ f1 = f1 * f2;
+ else if (op == '/')
+ {
+#ifdef VMS
+ // VMS crashes on divide by zero, work around it
+ if (f2 == 0.0)
+ {
+ if (f1 == 0)
+ f1 = -1 * __F_FLT_MAX - 1L; // similar to NaN
+ else if (f1 < 0)
+ f1 = -1 * __F_FLT_MAX;
else
- {
- if (op == '+')
- n1 = n1 + n2;
- else
- n1 = n1 - n2;
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = n1;
- }
+ f1 = __F_FLT_MAX;
}
- clear_tv(&var2);
+ else
+ f1 = f1 / f2;
+#else
+ // We rely on the floating point library to handle divide
+ // by zero to result in "inf" and not a crash.
+ f1 = f1 / f2;
+#endif
}
+ else
+ {
+ emsg(_(e_cannot_use_percent_with_float));
+ return FAIL;
+ }
+ tv1->v_type = VAR_FLOAT;
+ tv1->vval.v_float = f1;
+ }
+ else
+ {
+ int failed = FALSE;
+
+ if (op == '*')
+ n1 = n1 * n2;
+ else if (op == '/')
+ n1 = num_divide(n1, n2, &failed);
+ else
+ n1 = num_modulus(n1, n2, &failed);
+ if (failed)
+ return FAIL;
+
+ tv1->v_type = VAR_NUMBER;
+ tv1->vval.v_number = n1;
}
+
return OK;
}
@@ -3941,8 +4439,6 @@ eval7(
evalarg_T *evalarg,
int want_string) // after "." operator
{
- int use_float = FALSE;
-
/*
* Get the first expression.
*/
@@ -3959,9 +4455,6 @@ eval7(
typval_T var2;
char_u *p;
int op;
- varnumber_T n1, n2;
- float_T f1, f2;
- int error;
// "*=", "/=" and "%=" are assignments
p = eval_next_non_blank(*arg, evalarg, &getnext);
@@ -3983,26 +4476,6 @@ eval7(
*arg = p;
}
- f1 = 0;
- f2 = 0;
- error = FALSE;
- if (evaluate)
- {
- if (rettv->v_type == VAR_FLOAT)
- {
- f1 = rettv->vval.v_float;
- use_float = TRUE;
- n1 = 0;
- }
- else
- n1 = tv_get_number_chk(rettv, &error);
- clear_tv(rettv);
- if (error)
- return FAIL;
- }
- else
- n1 = 0;
-
/*
* Get the second variable.
*/
@@ -4017,81 +4490,9 @@ eval7(
return FAIL;
if (evaluate)
- {
- if (var2.v_type == VAR_FLOAT)
- {
- if (!use_float)
- {
- f1 = n1;
- use_float = TRUE;
- }
- f2 = var2.vval.v_float;
- n2 = 0;
- }
- else
- {
- n2 = tv_get_number_chk(&var2, &error);
- clear_tv(&var2);
- if (error)
- return FAIL;
- if (use_float)
- f2 = n2;
- }
-
- /*
- * Compute the result.
- * When either side is a float the result is a float.
- */
- if (use_float)
- {
- if (op == '*')
- f1 = f1 * f2;
- else if (op == '/')
- {
-#ifdef VMS
- // VMS crashes on divide by zero, work around it
- if (f2 == 0.0)
- {
- if (f1 == 0)
- f1 = -1 * __F_FLT_MAX - 1L; // similar to NaN
- else if (f1 < 0)
- f1 = -1 * __F_FLT_MAX;
- else
- f1 = __F_FLT_MAX;
- }
- else
- f1 = f1 / f2;
-#else
- // We rely on the floating point library to handle divide
- // by zero to result in "inf" and not a crash.
- f1 = f1 / f2;
-#endif
- }
- else
- {
- emsg(_(e_cannot_use_percent_with_float));
- return FAIL;
- }
- rettv->v_type = VAR_FLOAT;
- rettv->vval.v_float = f1;
- }
- else
- {
- int failed = FALSE;
-
- if (op == '*')
- n1 = n1 * n2;
- else if (op == '/')
- n1 = num_divide(n1, n2, &failed);
- else
- n1 = num_modulus(n1, n2, &failed);
- if (failed)
- return FAIL;
-
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = n1;
- }
- }
+ // Compute the result.
+ if (eval_multdiv_number(rettv, &var2, op) == FAIL)
+ return FAIL;
}
return OK;
@@ -4244,18 +4645,21 @@ handle_predefined(char_u *s, int len, typval_T *rettv)
case 9:
if (STRNCMP(s, "null_", 5) != 0)
break;
+ // null_list
if (STRNCMP(s + 5, "list", 4) == 0)
{
rettv->v_type = VAR_LIST;
rettv->vval.v_list = NULL;
return OK;
}
+ // null_dict
if (STRNCMP(s + 5, "dict", 4) == 0)
{
rettv->v_type = VAR_DICT;
rettv->vval.v_dict = NULL;
return OK;
}
+ // null_blob
if (STRNCMP(s + 5, "blob", 4) == 0)
{
rettv->v_type = VAR_BLOB;
@@ -4314,6 +4718,158 @@ handle_predefined(char_u *s, int len, typval_T *rettv)
}
/*
+ * Handle register contents: @r.
+ */
+ static void
+eval9_reg_contents(
+ char_u **arg,
+ typval_T *rettv,
+ int evaluate)
+{
+ int vim9script = in_vim9script();
+
+ ++*arg; // skip '@'
+ if (evaluate)
+ {
+ if (vim9script && IS_WHITE_OR_NUL(**arg))
+ semsg(_(e_syntax_error_at_str), *arg);
+ else if (vim9script && !valid_yank_reg(**arg, FALSE))
+ emsg_invreg(**arg);
+ else
+ {
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = get_reg_contents(**arg,
+ GREG_EXPR_SRC);
+ }
+ }
+ if (**arg != NUL)
+ ++*arg;
+}
+
+/*
+ * Handle a nested expression: (expression) or lambda: (arg) => expr
+ */
+ static int
+eval9_nested_expr(
+ char_u **arg,
+ typval_T *rettv,
+ evalarg_T *evalarg,
+ int evaluate)
+{
+ int ret = NOTDONE;
+ int vim9script = in_vim9script();
+
+ if (vim9script)
+ {
+ ret = get_lambda_tv(arg, rettv, TRUE, evalarg);
+ if (ret == OK && evaluate)
+ {
+ ufunc_T *ufunc = rettv->vval.v_partial->pt_func;
+
+ // Compile it here to get the return type. The return
+ // type is optional, when it's missing use t_unknown.
+ // This is recognized in compile_return().
+ if (ufunc->uf_ret_type->tt_type == VAR_VOID)
+ ufunc->uf_ret_type = &t_unknown;
+ if (compile_def_function(ufunc, FALSE,
+ get_compile_type(ufunc), NULL) == FAIL)
+ {
+ clear_tv(rettv);
+ ret = FAIL;
+ }
+ }
+ }
+ if (ret == NOTDONE)
+ {
+ *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
+ ret = eval1(arg, rettv, evalarg); // recursive!
+
+ *arg = skipwhite_and_linebreak(*arg, evalarg);
+ if (**arg == ')')
+ ++*arg;
+ else if (ret == OK)
+ {
+ emsg(_(e_missing_closing_paren));
+ clear_tv(rettv);
+ ret = FAIL;
+ }
+ }
+
+ return ret;
+}
+
+/*
+* Handle be a variable or function name.
+* Can also be a curly-braces kind of name: {expr}.
+*/
+ static int
+eval9_var_func_name(
+ char_u **arg,
+ typval_T *rettv,
+ evalarg_T *evalarg,
+ int evaluate,
+ char_u **name_start)
+{
+ char_u *s;
+ int len;
+ char_u *alias;
+ int ret = OK;
+ int vim9script = in_vim9script();
+
+ s = *arg;
+ len = get_name_len(arg, &alias, evaluate, TRUE);
+ if (alias != NULL)
+ s = alias;
+
+ if (len <= 0)
+ ret = FAIL;
+ else
+ {
+ int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
+
+ if (evaluate && vim9script && len == 1 && *s == '_')
+ {
+ emsg(_(e_cannot_use_underscore_here));
+ ret = FAIL;
+ }
+ else if (evaluate && vim9script && len > 2
+ && s[0] == 's' && s[1] == ':')
+ {
+ semsg(_(e_cannot_use_s_colon_in_vim9_script_str), s);
+ ret = FAIL;
+ }
+ else if ((vim9script ? **arg : *skipwhite(*arg)) == '(')
+ {
+ // "name(..." recursive!
+ *arg = skipwhite(*arg);
+ ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
+ }
+ else if (evaluate)
+ {
+ // get the value of "true", "false", etc. or a variable
+ ret = FAIL;
+ if (vim9script)
+ ret = handle_predefined(s, len, rettv);
+ if (ret == FAIL)
+ {
+ *name_start = s;
+ ret = eval_variable(s, len, 0, rettv, NULL,
+ EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT);
+ }
+ }
+ else
+ {
+ // skip the name
+ check_vars(s, len);
+ ret = OK;
+ }
+ }
+ vim_free(alias);
+
+ return ret;
+}
+
+/*
* Handle sixth level expression:
* number number constant
* 0zFFFFFFFF Blob constant
@@ -4352,12 +4908,9 @@ eval9(
{
int evaluate = evalarg != NULL
&& (evalarg->eval_flags & EVAL_EVALUATE);
- int len;
- char_u *s;
char_u *name_start = NULL;
char_u *start_leader, *end_leader;
int ret = OK;
- char_u *alias;
static int recurse = 0;
int vim9script = in_vim9script();
@@ -4440,19 +4993,9 @@ eval9(
break;
/*
- * Dictionary: #{key: val, key: val}
+ * Literal Dictionary: #{key: val, key: val}
*/
- case '#': if (vim9script)
- {
- ret = vim9_bad_comment(*arg) ? FAIL : NOTDONE;
- }
- else if ((*arg)[1] == '{')
- {
- ++*arg;
- ret = eval_dict(arg, rettv, evalarg, TRUE);
- }
- else
- ret = NOTDONE;
+ case '#': ret = eval_lit_dict(arg, rettv, evalarg);
break;
/*
@@ -4486,64 +5029,14 @@ eval9(
/*
* Register contents: @r.
*/
- case '@': ++*arg;
- if (evaluate)
- {
- if (vim9script && IS_WHITE_OR_NUL(**arg))
- semsg(_(e_syntax_error_at_str), *arg);
- else if (vim9script && !valid_yank_reg(**arg, FALSE))
- emsg_invreg(**arg);
- else
- {
- rettv->v_type = VAR_STRING;
- rettv->vval.v_string = get_reg_contents(**arg,
- GREG_EXPR_SRC);
- }
- }
- if (**arg != NUL)
- ++*arg;
+ case '@': eval9_reg_contents(arg, rettv, evaluate);
break;
/*
* nested expression: (expression).
* or lambda: (arg) => expr
*/
- case '(': ret = NOTDONE;
- if (vim9script)
- {
- ret = get_lambda_tv(arg, rettv, TRUE, evalarg);
- if (ret == OK && evaluate)
- {
- ufunc_T *ufunc = rettv->vval.v_partial->pt_func;
-
- // Compile it here to get the return type. The return
- // type is optional, when it's missing use t_unknown.
- // This is recognized in compile_return().
- if (ufunc->uf_ret_type->tt_type == VAR_VOID)
- ufunc->uf_ret_type = &t_unknown;
- if (compile_def_function(ufunc, FALSE,
- get_compile_type(ufunc), NULL) == FAIL)
- {
- clear_tv(rettv);
- ret = FAIL;
- }
- }
- }
- if (ret == NOTDONE)
- {
- *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
- ret = eval1(arg, rettv, evalarg); // recursive!
-
- *arg = skipwhite_and_linebreak(*arg, evalarg);
- if (**arg == ')')
- ++*arg;
- else if (ret == OK)
- {
- emsg(_(e_missing_closing_paren));
- clear_tv(rettv);
- ret = FAIL;
- }
- }
+ case '(': ret = eval9_nested_expr(arg, rettv, evalarg, evaluate);
break;
default: ret = NOTDONE;
@@ -4556,55 +5049,7 @@ eval9(
* Must be a variable or function name.
* Can also be a curly-braces kind of name: {expr}.
*/
- s = *arg;
- len = get_name_len(arg, &alias, evaluate, TRUE);
- if (alias != NULL)
- s = alias;
-
- if (len <= 0)
- ret = FAIL;
- else
- {
- int flags = evalarg == NULL ? 0 : evalarg->eval_flags;
-
- if (evaluate && vim9script && len == 1 && *s == '_')
- {
- emsg(_(e_cannot_use_underscore_here));
- ret = FAIL;
- }
- else if (evaluate && vim9script && len > 2
- && s[0] == 's' && s[1] == ':')
- {
- semsg(_(e_cannot_use_s_colon_in_vim9_script_str), s);
- ret = FAIL;
- }
- else if ((vim9script ? **arg : *skipwhite(*arg)) == '(')
- {
- // "name(..." recursive!
- *arg = skipwhite(*arg);
- ret = eval_func(arg, evalarg, s, len, rettv, flags, NULL);
- }
- else if (evaluate)
- {
- // get the value of "true", "false", etc. or a variable
- ret = FAIL;
- if (vim9script)
- ret = handle_predefined(s, len, rettv);
- if (ret == FAIL)
- {
- name_start = s;
- ret = eval_variable(s, len, 0, rettv, NULL,
- EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT);
- }
- }
- else
- {
- // skip the name
- check_vars(s, len);
- ret = OK;
- }
- }
- vim_free(alias);
+ ret = eval9_var_func_name(arg, rettv, evalarg, evaluate, &name_start);
}
// Handle following '[', '(' and '.' for expr[expr], expr.name,
@@ -4895,7 +5340,7 @@ eval_method(
{
*arg = name;
- // Truncate the name a the "(". Avoid trying to get another line
+ // Truncate the name at the "(". Avoid trying to get another line
// by making "getline" NULL.
*paren = NUL;
char_u *(*getline)(int, void *, int, getline_opt_T) = NULL;
@@ -4950,6 +5395,9 @@ eval_method(
clear_tv(&base);
vim_free(tofree);
+ if (alias != NULL)
+ vim_free(alias);
+
return ret;
}
@@ -5153,30 +5601,6 @@ check_can_index(typval_T *rettv, int evaluate, int verbose)
}
/*
- * slice() function
- */
- void
-f_slice(typval_T *argvars, typval_T *rettv)
-{
- if (in_vim9script()
- && ((argvars[0].v_type != VAR_STRING
- && argvars[0].v_type != VAR_LIST
- && argvars[0].v_type != VAR_BLOB
- && check_for_list_arg(argvars, 0) == FAIL)
- || check_for_number_arg(argvars, 1) == FAIL
- || check_for_opt_number_arg(argvars, 2) == FAIL))
- return;
-
- if (check_can_index(argvars, TRUE, FALSE) != OK)
- return;
-
- copy_tv(argvars, rettv);
- eval_index_inner(rettv, TRUE, argvars + 1,
- argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2,
- TRUE, NULL, 0, FALSE);
-}
-
-/*
* Apply index or range to "rettv".
* "var1" is the first index, NULL for [:expr].
* "var2" is the second index, NULL for [expr] and [expr: ]
@@ -5426,763 +5850,299 @@ partial_unref(partial_T *pt)
}
/*
- * Return the next (unique) copy ID.
- * Used for serializing nested structures.
- */
- int
-get_copyID(void)
-{
- current_copyID += COPYID_INC;
- return current_copyID;
-}
-
-/*
- * Garbage collection for lists and dictionaries.
- *
- * We use reference counts to be able to free most items right away when they
- * are no longer used. But for composite items it's possible that it becomes
- * unused while the reference count is > 0: When there is a recursive
- * reference. Example:
- * :let l = [1, 2, 3]
- * :let d = {9: l}
- * :let l[1] = d
- *
- * Since this is quite unusual we handle this with garbage collection: every
- * once in a while find out which lists and dicts are not referenced from any
- * variable.
- *
- * Here is a good reference text about garbage collection (refers to Python
- * but it applies to all reference-counting mechanisms):
- * http://python.ca/nas/python/gc/
- */
-
-/*
- * Do garbage collection for lists and dicts.
- * When "testing" is TRUE this is called from test_garbagecollect_now().
- * Return TRUE if some memory was freed.
+ * Return a textual representation of a string in "tv".
+ * If the memory is allocated "tofree" is set to it, otherwise NULL.
+ * When both "echo_style" and "composite_val" are FALSE, put quotes around
+ * strings as "string()", otherwise does not put quotes around strings.
+ * May return NULL.
*/
- int
-garbage_collect(int testing)
+ static char_u *
+string_tv2string(
+ typval_T *tv,
+ char_u **tofree,
+ int echo_style,
+ int composite_val)
{
- int copyID;
- int abort = FALSE;
- buf_T *buf;
- win_T *wp;
- int did_free = FALSE;
- tabpage_T *tp;
-
- if (!testing)
- {
- // Only do this once.
- want_garbage_collect = FALSE;
- may_garbage_collect = FALSE;
- garbage_collect_at_exit = FALSE;
- }
-
- // The execution stack can grow big, limit the size.
- if (exestack.ga_maxlen - exestack.ga_len > 500)
- {
- size_t new_len;
- char_u *pp;
- int n;
-
- // Keep 150% of the current size, with a minimum of the growth size.
- n = exestack.ga_len / 2;
- if (n < exestack.ga_growsize)
- n = exestack.ga_growsize;
-
- // Don't make it bigger though.
- if (exestack.ga_len + n < exestack.ga_maxlen)
- {
- new_len = (size_t)exestack.ga_itemsize * (exestack.ga_len + n);
- pp = vim_realloc(exestack.ga_data, new_len);
- if (pp == NULL)
- return FAIL;
- exestack.ga_maxlen = exestack.ga_len + n;
- exestack.ga_data = pp;
- }
- }
-
- // We advance by two because we add one for items referenced through
- // previous_funccal.
- copyID = get_copyID();
-
- /*
- * 1. Go through all accessible variables and mark all lists and dicts
- * with copyID.
- */
-
- // Don't free variables in the previous_funccal list unless they are only
- // referenced through previous_funccal. This must be first, because if
- // the item is referenced elsewhere the funccal must not be freed.
- abort = abort || set_ref_in_previous_funccal(copyID);
-
- // script-local variables
- abort = abort || garbage_collect_scriptvars(copyID);
-
- // buffer-local variables
- FOR_ALL_BUFFERS(buf)
- abort = abort || set_ref_in_item(&buf->b_bufvar.di_tv, copyID,
- NULL, NULL);
-
- // window-local variables
- FOR_ALL_TAB_WINDOWS(tp, wp)
- abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
- NULL, NULL);
- // window-local variables in autocmd windows
- for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
- if (aucmd_win[i].auc_win != NULL)
- abort = abort || set_ref_in_item(
- &aucmd_win[i].auc_win->w_winvar.di_tv, copyID, NULL, NULL);
-#ifdef FEAT_PROP_POPUP
- FOR_ALL_POPUPWINS(wp)
- abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
- NULL, NULL);
- FOR_ALL_TABPAGES(tp)
- FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
- abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
- NULL, NULL);
-#endif
-
- // tabpage-local variables
- FOR_ALL_TABPAGES(tp)
- abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID,
- NULL, NULL);
- // global variables
- abort = abort || garbage_collect_globvars(copyID);
-
- // function-local variables
- abort = abort || set_ref_in_call_stack(copyID);
-
- // named functions (matters for closures)
- abort = abort || set_ref_in_functions(copyID);
-
- // function call arguments, if v:testing is set.
- abort = abort || set_ref_in_func_args(copyID);
-
- // funcstacks keep variables for closures
- abort = abort || set_ref_in_funcstacks(copyID);
-
- // loopvars keep variables for loop blocks
- abort = abort || set_ref_in_loopvars(copyID);
-
- // v: vars
- abort = abort || garbage_collect_vimvars(copyID);
-
- // callbacks in buffers
- abort = abort || set_ref_in_buffers(copyID);
-
- // 'completefunc', 'omnifunc' and 'thesaurusfunc' callbacks
- abort = abort || set_ref_in_insexpand_funcs(copyID);
-
- // 'operatorfunc' callback
- abort = abort || set_ref_in_opfunc(copyID);
-
- // 'tagfunc' callback
- abort = abort || set_ref_in_tagfunc(copyID);
-
- // 'imactivatefunc' and 'imstatusfunc' callbacks
- abort = abort || set_ref_in_im_funcs(copyID);
-
-#ifdef FEAT_LUA
- abort = abort || set_ref_in_lua(copyID);
-#endif
-
-#ifdef FEAT_PYTHON
- abort = abort || set_ref_in_python(copyID);
-#endif
-
-#ifdef FEAT_PYTHON3
- abort = abort || set_ref_in_python3(copyID);
-#endif
-
-#ifdef FEAT_JOB_CHANNEL
- abort = abort || set_ref_in_channel(copyID);
- abort = abort || set_ref_in_job(copyID);
-#endif
-#ifdef FEAT_NETBEANS_INTG
- abort = abort || set_ref_in_nb_channel(copyID);
-#endif
-
-#ifdef FEAT_TIMERS
- abort = abort || set_ref_in_timer(copyID);
-#endif
-
-#ifdef FEAT_QUICKFIX
- abort = abort || set_ref_in_quickfix(copyID);
-#endif
-
-#ifdef FEAT_TERMINAL
- abort = abort || set_ref_in_term(copyID);
-#endif
-
-#ifdef FEAT_PROP_POPUP
- abort = abort || set_ref_in_popups(copyID);
-#endif
-
- abort = abort || set_ref_in_classes(copyID);
+ char_u *r = NULL;
- if (!abort)
+ if (echo_style && !composite_val)
{
- /*
- * 2. Free lists and dictionaries that are not referenced.
- */
- did_free = free_unref_items(copyID);
-
- /*
- * 3. Check if any funccal can be freed now.
- * This may call us back recursively.
- */
- free_unref_funccal(copyID, testing);
+ *tofree = NULL;
+ r = tv->vval.v_string;
+ if (r == NULL)
+ r = (char_u *)"";
}
- else if (p_verbose > 0)
+ else
{
- verb_msg(_("Not enough memory to set references, garbage collection aborted!"));
+ *tofree = string_quote(tv->vval.v_string, FALSE);
+ r = *tofree;
}
- return did_free;
-}
-
-/*
- * Free lists, dictionaries, channels and jobs that are no longer referenced.
- */
- static int
-free_unref_items(int copyID)
-{
- int did_free = FALSE;
-
- // Let all "free" functions know that we are here. This means no
- // dictionaries, lists, channels or jobs are to be freed, because we will
- // do that here.
- in_free_unref_items = TRUE;
-
- /*
- * PASS 1: free the contents of the items. We don't free the items
- * themselves yet, so that it is possible to decrement refcount counters
- */
-
- // Go through the list of dicts and free items without this copyID.
- did_free |= dict_free_nonref(copyID);
-
- // Go through the list of lists and free items without this copyID.
- did_free |= list_free_nonref(copyID);
-
- // Go through the list of objects and free items without this copyID.
- did_free |= object_free_nonref(copyID);
-
- // Go through the list of classes and free items without this copyID.
- did_free |= class_free_nonref(copyID);
-
-#ifdef FEAT_JOB_CHANNEL
- // Go through the list of jobs and free items without the copyID. This
- // must happen before doing channels, because jobs refer to channels, but
- // the reference from the channel to the job isn't tracked.
- did_free |= free_unused_jobs_contents(copyID, COPYID_MASK);
-
- // Go through the list of channels and free items without the copyID.
- did_free |= free_unused_channels_contents(copyID, COPYID_MASK);
-#endif
-
- /*
- * PASS 2: free the items themselves.
- */
- object_free_items(copyID);
- dict_free_items(copyID);
- list_free_items(copyID);
-
-#ifdef FEAT_JOB_CHANNEL
- // Go through the list of jobs and free items without the copyID. This
- // must happen before doing channels, because jobs refer to channels, but
- // the reference from the channel to the job isn't tracked.
- free_unused_jobs(copyID, COPYID_MASK);
-
- // Go through the list of channels and free items without the copyID.
- free_unused_channels(copyID, COPYID_MASK);
-#endif
-
- in_free_unref_items = FALSE;
-
- return did_free;
+ return r;
}
/*
- * Mark all lists and dicts referenced through hashtab "ht" with "copyID".
- * "list_stack" is used to add lists to be marked. Can be NULL.
- *
- * Returns TRUE if setting references failed somehow.
+ * Return a textual representation of a function in "tv".
+ * If the memory is allocated "tofree" is set to it, otherwise NULL.
+ * When "echo_style" is FALSE, put quotes around the function name as
+ * "function()", otherwise does not put quotes around function name.
+ * May return NULL.
*/
- int
-set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack)
+ static char_u *
+func_tv2string(typval_T *tv, char_u **tofree, int echo_style)
{
- int todo;
- int abort = FALSE;
- hashitem_T *hi;
- hashtab_T *cur_ht;
- ht_stack_T *ht_stack = NULL;
- ht_stack_T *tempitem;
+ char_u *r = NULL;
+ char_u buf[MAX_FUNC_NAME_LEN];
- cur_ht = ht;
- for (;;)
+ if (echo_style)
{
- if (!abort)
+ *tofree = NULL;
+
+ if (tv->vval.v_string == NULL)
+ r = (char_u *)"function()";
+ else
{
- // Mark each item in the hashtab. If the item contains a hashtab
- // it is added to ht_stack, if it contains a list it is added to
- // list_stack.
- todo = (int)cur_ht->ht_used;
- FOR_ALL_HASHTAB_ITEMS(cur_ht, hi, todo)
- if (!HASHITEM_EMPTY(hi))
- {
- --todo;
- abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID,
- &ht_stack, list_stack);
- }
+ r = make_ufunc_name_readable(tv->vval.v_string, buf,
+ MAX_FUNC_NAME_LEN);
+ if (r == buf)
+ r = *tofree = vim_strsave(buf);
}
+ }
+ else
+ {
+ char_u *s = NULL;
- if (ht_stack == NULL)
- break;
+ if (tv->vval.v_string != NULL)
+ s = make_ufunc_name_readable(tv->vval.v_string, buf,
+ MAX_FUNC_NAME_LEN);
- // take an item from the stack
- cur_ht = ht_stack->ht;
- tempitem = ht_stack;
- ht_stack = ht_stack->prev;
- free(tempitem);
+ r = *tofree = string_quote(s, TRUE);
}
- return abort;
+ return r;
}
-#if defined(FEAT_LUA) || defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) \
- || defined(PROTO)
/*
- * Mark a dict and its items with "copyID".
- * Returns TRUE if setting references failed somehow.
+ * Return a textual representation of a partial in "tv".
+ * If the memory is allocated "tofree" is set to it, otherwise NULL.
+ * "numbuf" is used for a number. May return NULL.
*/
- int
-set_ref_in_dict(dict_T *d, int copyID)
+ static char_u *
+partial_tv2string(
+ typval_T *tv,
+ char_u **tofree,
+ char_u *numbuf,
+ int copyID)
{
- if (d != NULL && d->dv_copyID != copyID)
- {
- d->dv_copyID = copyID;
- return set_ref_in_ht(&d->dv_hashtab, copyID, NULL);
- }
- return FALSE;
-}
-#endif
+ char_u *r = NULL;
+ partial_T *pt;
+ char_u *fname;
+ garray_T ga;
+ int i;
+ char_u *tf;
-/*
- * Mark a list and its items with "copyID".
- * Returns TRUE if setting references failed somehow.
- */
- int
-set_ref_in_list(list_T *ll, int copyID)
-{
- if (ll != NULL && ll->lv_copyID != copyID)
+ pt = tv->vval.v_partial;
+ fname = string_quote(pt == NULL ? NULL : partial_name(pt), FALSE);
+
+ ga_init2(&ga, 1, 100);
+ ga_concat(&ga, (char_u *)"function(");
+ if (fname != NULL)
{
- ll->lv_copyID = copyID;
- return set_ref_in_list_items(ll, copyID, NULL);
+ // When using uf_name prepend "g:" for a global function.
+ if (pt != NULL && pt->pt_name == NULL && fname[0] == '\''
+ && vim_isupper(fname[1]))
+ {
+ ga_concat(&ga, (char_u *)"'g:");
+ ga_concat(&ga, fname + 1);
+ }
+ else
+ ga_concat(&ga, fname);
+ vim_free(fname);
}
- return FALSE;
-}
-
-/*
- * Mark all lists and dicts referenced through list "l" with "copyID".
- * "ht_stack" is used to add hashtabs to be marked. Can be NULL.
- *
- * Returns TRUE if setting references failed somehow.
- */
- int
-set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack)
-{
- listitem_T *li;
- int abort = FALSE;
- list_T *cur_l;
- list_stack_T *list_stack = NULL;
- list_stack_T *tempitem;
-
- cur_l = l;
- for (;;)
+ if (pt != NULL && pt->pt_argc > 0)
{
- if (!abort && cur_l->lv_first != &range_list_item)
- // Mark each item in the list. If the item contains a hashtab
- // it is added to ht_stack, if it contains a list it is added to
- // list_stack.
- for (li = cur_l->lv_first; !abort && li != NULL; li = li->li_next)
- abort = abort || set_ref_in_item(&li->li_tv, copyID,
- ht_stack, &list_stack);
- if (list_stack == NULL)
- break;
-
- // take an item from the stack
- cur_l = list_stack->list;
- tempitem = list_stack;
- list_stack = list_stack->prev;
- free(tempitem);
+ ga_concat(&ga, (char_u *)", [");
+ for (i = 0; i < pt->pt_argc; ++i)
+ {
+ if (i > 0)
+ ga_concat(&ga, (char_u *)", ");
+ ga_concat(&ga, tv2string(&pt->pt_argv[i], &tf, numbuf, copyID));
+ vim_free(tf);
+ }
+ ga_concat(&ga, (char_u *)"]");
}
-
- return abort;
-}
-
-/*
- * Mark the partial in callback 'cb' with "copyID".
- */
- int
-set_ref_in_callback(callback_T *cb, int copyID)
-{
- typval_T tv;
-
- if (cb->cb_name == NULL || *cb->cb_name == NUL || cb->cb_partial == NULL)
- return FALSE;
-
- tv.v_type = VAR_PARTIAL;
- tv.vval.v_partial = cb->cb_partial;
- return set_ref_in_item(&tv, copyID, NULL, NULL);
-}
-
-/*
- * Mark the dict "dd" with "copyID".
- * Also see set_ref_in_item().
- */
- static int
-set_ref_in_item_dict(
- dict_T *dd,
- int copyID,
- ht_stack_T **ht_stack,
- list_stack_T **list_stack)
-{
- if (dd == NULL || dd->dv_copyID == copyID)
- return FALSE;
-
- // Didn't see this dict yet.
- dd->dv_copyID = copyID;
- if (ht_stack == NULL)
- return set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack);
-
- ht_stack_T *newitem = ALLOC_ONE(ht_stack_T);
- if (newitem == NULL)
- return TRUE;
-
- newitem->ht = &dd->dv_hashtab;
- newitem->prev = *ht_stack;
- *ht_stack = newitem;
-
- return FALSE;
-}
-
-/*
- * Mark the list "ll" with "copyID".
- * Also see set_ref_in_item().
- */
- static int
-set_ref_in_item_list(
- list_T *ll,
- int copyID,
- ht_stack_T **ht_stack,
- list_stack_T **list_stack)
-{
- if (ll == NULL || ll->lv_copyID == copyID)
- return FALSE;
-
- // Didn't see this list yet.
- ll->lv_copyID = copyID;
- if (list_stack == NULL)
- return set_ref_in_list_items(ll, copyID, ht_stack);
-
- list_stack_T *newitem = ALLOC_ONE(list_stack_T);
- if (newitem == NULL)
- return TRUE;
-
- newitem->list = ll;
- newitem->prev = *list_stack;
- *list_stack = newitem;
-
- return FALSE;
-}
-
-/*
- * Mark the partial "pt" with "copyID".
- * Also see set_ref_in_item().
- */
- static int
-set_ref_in_item_partial(
- partial_T *pt,
- int copyID,
- ht_stack_T **ht_stack,
- list_stack_T **list_stack)
-{
- if (pt == NULL || pt->pt_copyID == copyID)
- return FALSE;
-
- // Didn't see this partial yet.
- pt->pt_copyID = copyID;
-
- int abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
-
- if (pt->pt_dict != NULL)
+ if (pt != NULL && pt->pt_dict != NULL)
{
typval_T dtv;
+ ga_concat(&ga, (char_u *)", ");
dtv.v_type = VAR_DICT;
dtv.vval.v_dict = pt->pt_dict;
- set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ ga_concat(&ga, tv2string(&dtv, &tf, numbuf, copyID));
+ vim_free(tf);
}
+ // terminate with ')' and a NUL
+ ga_concat_len(&ga, (char_u *)")", 2);
- if (pt->pt_obj != NULL)
- {
- typval_T objtv;
-
- objtv.v_type = VAR_OBJECT;
- objtv.vval.v_object = pt->pt_obj;
- set_ref_in_item(&objtv, copyID, ht_stack, list_stack);
- }
+ *tofree = ga.ga_data;
+ r = *tofree;
- for (int i = 0; i < pt->pt_argc; ++i)
- abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
- ht_stack, list_stack);
- // pt_funcstack is handled in set_ref_in_funcstacks()
- // pt_loopvars is handled in set_ref_in_loopvars()
-
- return abort;
+ return r;
}
-#ifdef FEAT_JOB_CHANNEL
/*
- * Mark the job "pt" with "copyID".
- * Also see set_ref_in_item().
+ * Return a textual representation of a List in "tv".
+ * If the memory is allocated "tofree" is set to it, otherwise NULL.
+ * When "copyID" is not zero replace recursive lists with "...". When
+ * "restore_copyID" is FALSE, repeated items in lists are replaced with "...".
+ * May return NULL.
*/
- static int
-set_ref_in_item_job(
- job_T *job,
- int copyID,
- ht_stack_T **ht_stack,
- list_stack_T **list_stack)
+ static char_u *
+list_tv2string(
+ typval_T *tv,
+ char_u **tofree,
+ int copyID,
+ int restore_copyID)
{
- typval_T dtv;
-
- if (job == NULL || job->jv_copyID == copyID)
- return FALSE;
+ char_u *r = NULL;
- job->jv_copyID = copyID;
- if (job->jv_channel != NULL)
+ if (tv->vval.v_list == NULL)
{
- dtv.v_type = VAR_CHANNEL;
- dtv.vval.v_channel = job->jv_channel;
- set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ // NULL list is equivalent to empty list.
+ *tofree = NULL;
+ r = (char_u *)"[]";
}
- if (job->jv_exit_cb.cb_partial != NULL)
+ else if (copyID != 0 && tv->vval.v_list->lv_copyID == copyID
+ && tv->vval.v_list->lv_len > 0)
{
- dtv.v_type = VAR_PARTIAL;
- dtv.vval.v_partial = job->jv_exit_cb.cb_partial;
- set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ *tofree = NULL;
+ r = (char_u *)"[...]";
}
+ else
+ {
+ int old_copyID;
+ if (restore_copyID)
+ old_copyID = tv->vval.v_list->lv_copyID;
- return FALSE;
+ tv->vval.v_list->lv_copyID = copyID;
+ *tofree = list2string(tv, copyID, restore_copyID);
+ if (restore_copyID)
+ tv->vval.v_list->lv_copyID = old_copyID;
+ r = *tofree;
+ }
+
+ return r;
}
/*
- * Mark the channel "ch" with "copyID".
- * Also see set_ref_in_item().
+ * Return a textual representation of a Dict in "tv".
+ * If the memory is allocated "tofree" is set to it, otherwise NULL.
+ * When "copyID" is not zero replace recursive dicts with "...".
+ * When "restore_copyID" is FALSE, repeated items in the dictionary are
+ * replaced with "...". May return NULL.
*/
- static int
-set_ref_in_item_channel(
- channel_T *ch,
- int copyID,
- ht_stack_T **ht_stack,
- list_stack_T **list_stack)
+ static char_u *
+dict_tv2string(
+ typval_T *tv,
+ char_u **tofree,
+ int copyID,
+ int restore_copyID)
{
- typval_T dtv;
-
- if (ch == NULL || ch->ch_copyID == copyID)
- return FALSE;
+ char_u *r = NULL;
- ch->ch_copyID = copyID;
- for (ch_part_T part = PART_SOCK; part < PART_COUNT; ++part)
+ if (tv->vval.v_dict == NULL)
{
- for (jsonq_T *jq = ch->ch_part[part].ch_json_head.jq_next;
- jq != NULL; jq = jq->jq_next)
- set_ref_in_item(jq->jq_value, copyID, ht_stack, list_stack);
- for (cbq_T *cq = ch->ch_part[part].ch_cb_head.cq_next; cq != NULL;
- cq = cq->cq_next)
- if (cq->cq_callback.cb_partial != NULL)
- {
- dtv.v_type = VAR_PARTIAL;
- dtv.vval.v_partial = cq->cq_callback.cb_partial;
- set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
- }
- if (ch->ch_part[part].ch_callback.cb_partial != NULL)
- {
- dtv.v_type = VAR_PARTIAL;
- dtv.vval.v_partial = ch->ch_part[part].ch_callback.cb_partial;
- set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
- }
+ // NULL dict is equivalent to empty dict.
+ *tofree = NULL;
+ r = (char_u *)"{}";
}
- if (ch->ch_callback.cb_partial != NULL)
+ else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID
+ && tv->vval.v_dict->dv_hashtab.ht_used != 0)
{
- dtv.v_type = VAR_PARTIAL;
- dtv.vval.v_partial = ch->ch_callback.cb_partial;
- set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ *tofree = NULL;
+ r = (char_u *)"{...}";
}
- if (ch->ch_close_cb.cb_partial != NULL)
+ else
{
- dtv.v_type = VAR_PARTIAL;
- dtv.vval.v_partial = ch->ch_close_cb.cb_partial;
- set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
- }
-
- return FALSE;
-}
-#endif
+ int old_copyID;
+ if (restore_copyID)
+ old_copyID = tv->vval.v_dict->dv_copyID;
-/*
- * Mark the class "cl" with "copyID".
- * Also see set_ref_in_item().
- */
- int
-set_ref_in_item_class(
- class_T *cl,
- int copyID,
- ht_stack_T **ht_stack,
- list_stack_T **list_stack)
-{
- int abort = FALSE;
-
- if (cl == NULL || cl->class_copyID == copyID)
- return FALSE;
-
- cl->class_copyID = copyID;
- if (cl->class_members_tv != NULL)
- {
- // The "class_members_tv" table is allocated only for regular classes
- // and not for interfaces.
- for (int i = 0; !abort && i < cl->class_class_member_count; ++i)
- abort = abort || set_ref_in_item(
- &cl->class_members_tv[i],
- copyID, ht_stack, list_stack);
+ tv->vval.v_dict->dv_copyID = copyID;
+ *tofree = dict2string(tv, copyID, restore_copyID);
+ if (restore_copyID)
+ tv->vval.v_dict->dv_copyID = old_copyID;
+ r = *tofree;
}
- for (int i = 0; !abort && i < cl->class_class_function_count; ++i)
- abort = abort || set_ref_in_func(NULL,
- cl->class_class_functions[i], copyID);
-
- for (int i = 0; !abort && i < cl->class_obj_method_count; ++i)
- abort = abort || set_ref_in_func(NULL,
- cl->class_obj_methods[i], copyID);
-
- return abort;
+ return r;
}
/*
- * Mark the object "cl" with "copyID".
- * Also see set_ref_in_item().
+ * Return a textual representation of a job or a channel in "tv".
+ * If the memory is allocated "tofree" is set to it, otherwise NULL.
+ * "numbuf" is used for a number.
+ * When "composite_val" is FALSE, put quotes around strings as "string()",
+ * otherwise does not put quotes around strings.
+ * May return NULL.
*/
- static int
-set_ref_in_item_object(
- object_T *obj,
- int copyID,
- ht_stack_T **ht_stack,
- list_stack_T **list_stack)
+ static char_u *
+jobchan_tv2string(
+ typval_T *tv,
+ char_u **tofree,
+ char_u *numbuf,
+ int composite_val)
{
- int abort = FALSE;
+ char_u *r = NULL;
- if (obj == NULL || obj->obj_copyID == copyID)
- return FALSE;
+#ifdef FEAT_JOB_CHANNEL
+ *tofree = NULL;
- obj->obj_copyID = copyID;
+ if (tv->v_type == VAR_JOB)
+ r = job_to_string_buf(tv, numbuf);
+ else
+ r = channel_to_string_buf(tv, numbuf);
- // The typval_T array is right after the object_T.
- typval_T *mtv = (typval_T *)(obj + 1);
- for (int i = 0; !abort
- && i < obj->obj_class->class_obj_member_count; ++i)
- abort = abort || set_ref_in_item(mtv + i, copyID,
- ht_stack, list_stack);
+ if (composite_val)
+ {
+ *tofree = string_quote(r, FALSE);
+ r = *tofree;
+ }
+#endif
- return abort;
+ return r;
}
/*
- * Mark all lists, dicts and other container types referenced through typval
- * "tv" with "copyID".
- * "list_stack" is used to add lists to be marked. Can be NULL.
- * "ht_stack" is used to add hashtabs to be marked. Can be NULL.
- *
- * Returns TRUE if setting references failed somehow.
+ * Return a textual representation of a class in "tv".
+ * If the memory is allocated "tofree" is set to it, otherwise NULL.
+ * May return NULL.
*/
- int
-set_ref_in_item(
- typval_T *tv,
- int copyID,
- ht_stack_T **ht_stack,
- list_stack_T **list_stack)
+ static char_u *
+class_tv2string(typval_T *tv, char_u **tofree)
{
- int abort = FALSE;
-
- switch (tv->v_type)
- {
- case VAR_DICT:
- return set_ref_in_item_dict(tv->vval.v_dict, copyID,
- ht_stack, list_stack);
-
- case VAR_LIST:
- return set_ref_in_item_list(tv->vval.v_list, copyID,
- ht_stack, list_stack);
-
- case VAR_FUNC:
- {
- abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
- break;
- }
-
- case VAR_PARTIAL:
- return set_ref_in_item_partial(tv->vval.v_partial, copyID,
- ht_stack, list_stack);
-
- case VAR_JOB:
-#ifdef FEAT_JOB_CHANNEL
- return set_ref_in_item_job(tv->vval.v_job, copyID,
- ht_stack, list_stack);
-#else
- break;
-#endif
-
- case VAR_CHANNEL:
-#ifdef FEAT_JOB_CHANNEL
- return set_ref_in_item_channel(tv->vval.v_channel, copyID,
- ht_stack, list_stack);
-#else
- break;
-#endif
-
- case VAR_CLASS:
- return set_ref_in_item_class(tv->vval.v_class, copyID,
- ht_stack, list_stack);
-
- case VAR_OBJECT:
- return set_ref_in_item_object(tv->vval.v_object, copyID,
- ht_stack, list_stack);
-
- case VAR_UNKNOWN:
- case VAR_ANY:
- case VAR_VOID:
- case VAR_BOOL:
- case VAR_SPECIAL:
- case VAR_NUMBER:
- case VAR_FLOAT:
- case VAR_STRING:
- case VAR_BLOB:
- case VAR_TYPEALIAS:
- case VAR_INSTR:
- // Types that do not contain any other item
- break;
- }
+ char_u *r = NULL;
+ class_T *cl = tv->vval.v_class;
+ char *s = "class";
+
+ if (cl != NULL && IS_INTERFACE(cl))
+ s = "interface";
+ else if (cl != NULL && IS_ENUM(cl))
+ s = "enum";
+ size_t len = STRLEN(s) + 1 +
+ (cl == NULL ? 9 : STRLEN(cl->class_name)) + 1;
+ r = *tofree = alloc(len);
+ vim_snprintf((char *)r, len, "%s %s", s,
+ cl == NULL ? "[unknown]" : (char *)cl->class_name);
- return abort;
+ return r;
}
/*
* Return a string with the string representation of a variable.
* If the memory is allocated "tofree" is set to it, otherwise NULL.
* "numbuf" is used for a number.
- * When "copyID" is not NULL replace recursive lists and dicts with "...".
+ * When "copyID" is not zero replace recursive lists and dicts with "...".
* When both "echo_style" and "composite_val" are FALSE, put quotes around
* strings as "string()", otherwise does not put quotes around strings, as
* ":echo" displays values.
@@ -6221,155 +6181,27 @@ echo_string_core(
switch (tv->v_type)
{
case VAR_STRING:
- if (echo_style && !composite_val)
- {
- *tofree = NULL;
- r = tv->vval.v_string;
- if (r == NULL)
- r = (char_u *)"";
- }
- else
- {
- *tofree = string_quote(tv->vval.v_string, FALSE);
- r = *tofree;
- }
+ r = string_tv2string(tv, tofree, echo_style, composite_val);
break;
case VAR_FUNC:
- {
- char_u buf[MAX_FUNC_NAME_LEN];
-
- if (echo_style)
- {
- r = tv->vval.v_string == NULL ? (char_u *)"function()"
- : make_ufunc_name_readable(tv->vval.v_string,
- buf, MAX_FUNC_NAME_LEN);
- if (r == buf)
- {
- r = vim_strsave(buf);
- *tofree = r;
- }
- else
- *tofree = NULL;
- }
- else
- {
- *tofree = string_quote(tv->vval.v_string == NULL ? NULL
- : make_ufunc_name_readable(
- tv->vval.v_string, buf, MAX_FUNC_NAME_LEN),
- TRUE);
- r = *tofree;
- }
- }
+ r = func_tv2string(tv, tofree, echo_style);
break;
case VAR_PARTIAL:
- {
- partial_T *pt = tv->vval.v_partial;
- char_u *fname = string_quote(pt == NULL ? NULL
- : partial_name(pt), FALSE);
- garray_T ga;
- int i;
- char_u *tf;
-
- ga_init2(&ga, 1, 100);
- ga_concat(&ga, (char_u *)"function(");
- if (fname != NULL)
- {
- // When using uf_name prepend "g:" for a global function.
- if (pt != NULL && pt->pt_name == NULL && fname[0] == '\''
- && vim_isupper(fname[1]))
- {
- ga_concat(&ga, (char_u *)"'g:");
- ga_concat(&ga, fname + 1);
- }
- else
- ga_concat(&ga, fname);
- vim_free(fname);
- }
- if (pt != NULL && pt->pt_argc > 0)
- {
- ga_concat(&ga, (char_u *)", [");
- for (i = 0; i < pt->pt_argc; ++i)
- {
- if (i > 0)
- ga_concat(&ga, (char_u *)", ");
- ga_concat(&ga,
- tv2string(&pt->pt_argv[i], &tf, numbuf, copyID));
- vim_free(tf);
- }
- ga_concat(&ga, (char_u *)"]");
- }
- if (pt != NULL && pt->pt_dict != NULL)
- {
- typval_T dtv;
-
- ga_concat(&ga, (char_u *)", ");
- dtv.v_type = VAR_DICT;
- dtv.vval.v_dict = pt->pt_dict;
- ga_concat(&ga, tv2string(&dtv, &tf, numbuf, copyID));
- vim_free(tf);
- }
- // terminate with ')' and a NUL
- ga_concat_len(&ga, (char_u *)")", 2);
-
- *tofree = ga.ga_data;
- r = *tofree;
- break;
- }
+ r = partial_tv2string(tv, tofree, numbuf, copyID);
+ break;
case VAR_BLOB:
r = blob2string(tv->vval.v_blob, tofree, numbuf);
break;
case VAR_LIST:
- if (tv->vval.v_list == NULL)
- {
- // NULL list is equivalent to empty list.
- *tofree = NULL;
- r = (char_u *)"[]";
- }
- else if (copyID != 0 && tv->vval.v_list->lv_copyID == copyID
- && tv->vval.v_list->lv_len > 0)
- {
- *tofree = NULL;
- r = (char_u *)"[...]";
- }
- else
- {
- int old_copyID = tv->vval.v_list->lv_copyID;
-
- tv->vval.v_list->lv_copyID = copyID;
- *tofree = list2string(tv, copyID, restore_copyID);
- if (restore_copyID)
- tv->vval.v_list->lv_copyID = old_copyID;
- r = *tofree;
- }
+ r = list_tv2string(tv, tofree, copyID, restore_copyID);
break;
case VAR_DICT:
- if (tv->vval.v_dict == NULL)
- {
- // NULL dict is equivalent to empty dict.
- *tofree = NULL;
- r = (char_u *)"{}";
- }
- else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID
- && tv->vval.v_dict->dv_hashtab.ht_used != 0)
- {
- *tofree = NULL;
- r = (char_u *)"{...}";
- }
- else
- {
- int old_copyID = tv->vval.v_dict->dv_copyID;
-
- tv->vval.v_dict->dv_copyID = copyID;
- *tofree = dict2string(tv, copyID, restore_copyID);
- if (restore_copyID)
- tv->vval.v_dict->dv_copyID = old_copyID;
- r = *tofree;
- }
+ r = dict_tv2string(tv, tofree, copyID, restore_copyID);
break;
case VAR_NUMBER:
@@ -6382,16 +6214,7 @@ echo_string_core(
case VAR_JOB:
case VAR_CHANNEL:
-#ifdef FEAT_JOB_CHANNEL
- *tofree = NULL;
- r = tv->v_type == VAR_JOB ? job_to_string_buf(tv, numbuf)
- : channel_to_string_buf(tv, numbuf);
- if (composite_val)
- {
- *tofree = string_quote(r, FALSE);
- r = *tofree;
- }
-#endif
+ r = jobchan_tv2string(tv, tofree, numbuf, composite_val);
break;
case VAR_INSTR:
@@ -6400,23 +6223,11 @@ echo_string_core(
break;
case VAR_CLASS:
- {
- class_T *cl = tv->vval.v_class;
- char *s = "class";
- if (IS_INTERFACE(cl))
- s = "interface";
- else if (IS_ENUM(cl))
- s = "enum";
- size_t len = STRLEN(s) + 1 +
- (cl == NULL ? 9 : STRLEN(cl->class_name)) + 1;
- r = *tofree = alloc(len);
- vim_snprintf((char *)r, len, "%s %s", s,
- cl == NULL ? "[unknown]" : (char *)cl->class_name);
- }
+ r = class_tv2string(tv, tofree);
break;
case VAR_OBJECT:
- *tofree = r = object_string(tv->vval.v_object, numbuf, copyID,
+ *tofree = r = object2string(tv->vval.v_object, numbuf, copyID,
echo_style, restore_copyID,
composite_val);
break;
@@ -6451,7 +6262,7 @@ echo_string_core(
* If the memory is allocated "tofree" is set to it, otherwise NULL.
* "numbuf" is used for a number.
* Does not put quotes around strings, as ":echo" displays values.
- * When "copyID" is not NULL replace recursive lists and dicts with "...".
+ * When "copyID" is not zero replace recursive lists and dicts with "...".
* May return NULL.
*/
char_u *
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 2064982..9720691 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -73,6 +73,7 @@ static void f_getpos(typval_T *argvars, typval_T *rettv);
static void f_getreg(typval_T *argvars, typval_T *rettv);
static void f_getreginfo(typval_T *argvars, typval_T *rettv);
static void f_getregion(typval_T *argvars, typval_T *rettv);
+static void f_getregionpos(typval_T *argvars, typval_T *rettv);
static void f_getregtype(typval_T *argvars, typval_T *rettv);
static void f_gettagstack(typval_T *argvars, typval_T *rettv);
static void f_gettext(typval_T *argvars, typval_T *rettv);
@@ -2006,6 +2007,8 @@ static funcentry_T global_functions[] =
ret_void, f_feedkeys},
{"file_readable", 1, 1, FEARG_1, arg1_string, // obsolete
ret_number_bool, f_filereadable},
+ {"filecopy", 2, 2, FEARG_1, arg2_string,
+ ret_number_bool, f_filecopy},
{"filereadable", 1, 1, FEARG_1, arg1_string,
ret_number_bool, f_filereadable},
{"filewritable", 1, 1, FEARG_1, arg1_string,
@@ -2136,6 +2139,8 @@ static funcentry_T global_functions[] =
ret_dict_any, f_getreginfo},
{"getregion", 2, 3, FEARG_1, arg3_list_list_dict,
ret_list_string, f_getregion},
+ {"getregionpos", 2, 3, FEARG_1, arg3_list_list_dict,
+ ret_list_string, f_getregionpos},
{"getregtype", 0, 1, FEARG_1, arg1_string,
ret_string, f_getregtype},
{"getscriptinfo", 0, 1, 0, arg1_dict_any,
@@ -3840,8 +3845,9 @@ set_cursorpos(typval_T *argvars, typval_T *rettv, int charcol)
return; // type error; errmsg already given
if (lnum > 0)
curwin->w_cursor.lnum = lnum;
- if (col > 0)
- curwin->w_cursor.col = col - 1;
+ if (col != MAXCOL && --col < 0)
+ col = 0;
+ curwin->w_cursor.col = col;
curwin->w_cursor.coladd = coladd;
// Make sure the cursor is in a valid position.
@@ -3958,7 +3964,7 @@ f_empty(typval_T *argvars, typval_T *rettv)
|| *argvars[0].vval.v_string == NUL;
break;
case VAR_PARTIAL:
- n = FALSE;
+ n = argvars[0].vval.v_partial == NULL;
break;
case VAR_NUMBER:
n = argvars[0].vval.v_number == 0;
@@ -4454,7 +4460,7 @@ f_exists_compiled(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
f_expand(typval_T *argvars, typval_T *rettv)
{
char_u *s;
- int len;
+ size_t len;
int options = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND;
expand_T xpc;
int error = FALSE;
@@ -5460,7 +5466,7 @@ f_getpos(typval_T *argvars, typval_T *rettv)
/*
* Convert from block_def to string
*/
- static char_u *
+ static char_u *
block_def2str(struct block_def *bd)
{
char_u *p, *ret;
@@ -5480,40 +5486,36 @@ block_def2str(struct block_def *bd)
return ret;
}
-/*
- * "getregion()" function
- */
- static void
-f_getregion(typval_T *argvars, typval_T *rettv)
-{
- linenr_T lnum;
- oparg_T oa;
- struct block_def bd;
- char_u *akt = NULL;
- int inclusive = TRUE;
- int fnum1 = -1, fnum2 = -1;
- pos_T p1, p2;
- char_u *type;
- buf_T *save_curbuf;
- buf_T *findbuf;
- char_u default_type[] = "v";
- int save_virtual;
- int l;
- int region_type = -1;
- int is_select_exclusive;
+ static int
+getregionpos(
+ typval_T *argvars,
+ typval_T *rettv,
+ pos_T *p1,
+ pos_T *p2,
+ int *inclusive,
+ int *region_type,
+ oparg_T *oap)
+{
+ int fnum1 = -1, fnum2 = -1;
+ char_u *type;
+ buf_T *findbuf;
+ char_u default_type[] = "v";
+ int block_width = 0;
+ int is_select_exclusive;
+ int l;
if (rettv_list_alloc(rettv) == FAIL)
- return;
+ return FAIL;
if (check_for_list_arg(argvars, 0) == FAIL
|| check_for_list_arg(argvars, 1) == FAIL
|| check_for_opt_dict_arg(argvars, 2) == FAIL)
- return;
+ return FAIL;
- if (list2fpos(&argvars[0], &p1, &fnum1, NULL, FALSE) != OK
- || list2fpos(&argvars[1], &p2, &fnum2, NULL, FALSE) != OK
+ if (list2fpos(&argvars[0], p1, &fnum1, NULL, FALSE) != OK
+ || list2fpos(&argvars[1], p2, &fnum2, NULL, FALSE) != OK
|| fnum1 != fnum2)
- return;
+ return FAIL;
if (argvars[2].v_type == VAR_DICT)
{
@@ -5531,120 +5533,142 @@ f_getregion(typval_T *argvars, typval_T *rettv)
}
if (type[0] == 'v' && type[1] == NUL)
- region_type = MCHAR;
+ *region_type = MCHAR;
else if (type[0] == 'V' && type[1] == NUL)
- region_type = MLINE;
- else if (type[0] == Ctrl_V && type[1] == NUL)
- region_type = MBLOCK;
+ *region_type = MLINE;
+ else if (type[0] == Ctrl_V)
+ {
+ char_u *p = type + 1;
+
+ if (*p != NUL && ((block_width = getdigits(&p)) <= 0 || *p != NUL))
+ {
+ semsg(_(e_invalid_value_for_argument_str_str), "type", type);
+ return FAIL;
+ }
+ *region_type = MBLOCK;
+ }
else
{
semsg(_(e_invalid_value_for_argument_str_str), "type", type);
- return;
+ return FAIL;
}
findbuf = fnum1 != 0 ? buflist_findnr(fnum1) : curbuf;
if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL)
{
emsg(_(e_buffer_is_not_loaded));
- return;
+ return FAIL;
}
- if (p1.lnum < 1 || p1.lnum > findbuf->b_ml.ml_line_count)
+ if (p1->lnum < 1 || p1->lnum > findbuf->b_ml.ml_line_count)
{
- semsg(_(e_invalid_line_number_nr), p1.lnum);
- return;
+ semsg(_(e_invalid_line_number_nr), p1->lnum);
+ return FAIL;
}
- if (p1.col < 1 || p1.col > ml_get_buf_len(findbuf, p1.lnum) + 1)
+ if (p1->col == MAXCOL)
+ p1->col = ml_get_buf_len(findbuf, p1->lnum) + 1;
+ else if (p1->col < 1 || p1->col > ml_get_buf_len(findbuf, p1->lnum) + 1)
{
- semsg(_(e_invalid_column_number_nr), p1.col);
- return;
+ semsg(_(e_invalid_column_number_nr), p1->col);
+ return FAIL;
}
- if (p2.lnum < 1 || p2.lnum > findbuf->b_ml.ml_line_count)
+
+ if (p2->lnum < 1 || p2->lnum > findbuf->b_ml.ml_line_count)
{
- semsg(_(e_invalid_line_number_nr), p2.lnum);
- return;
+ semsg(_(e_invalid_line_number_nr), p2->lnum);
+ return FAIL;
}
- if (p2.col < 1 || p2.col > ml_get_buf_len(findbuf, p2.lnum) + 1)
+ if (p2->col == MAXCOL)
+ p2->col = ml_get_buf_len(findbuf, p2->lnum) + 1;
+ else if (p2->col < 1 || p2->col > ml_get_buf_len(findbuf, p2->lnum) + 1)
{
- semsg(_(e_invalid_column_number_nr), p2.col);
- return;
+ semsg(_(e_invalid_column_number_nr), p2->col);
+ return FAIL;
}
- save_curbuf = curbuf;
curbuf = findbuf;
curwin->w_buffer = curbuf;
- save_virtual = virtual_op;
virtual_op = virtual_active();
- // NOTE: Adjust is needed.
- p1.col--;
- p2.col--;
+ // NOTE: Adjustment is needed.
+ p1->col--;
+ p2->col--;
- if (!LT_POS(p1, p2))
+ if (!LT_POS(*p1, *p2))
{
// swap position
pos_T p;
- p = p1;
- p1 = p2;
- p2 = p;
+ p = *p1;
+ *p1 = *p2;
+ *p2 = p;
}
- if (region_type == MCHAR)
+ if (*region_type == MCHAR)
{
- // handle 'selection' == "exclusive"
- if (is_select_exclusive && !EQUAL_POS(p1, p2))
- {
- if (p2.coladd > 0)
- p2.coladd--;
- else if (p2.col > 0)
- {
- p2.col--;
-
- mb_adjustpos(curbuf, &p2);
- }
- else if (p2.lnum > 1)
- {
- p2.lnum--;
- p2.col = ml_get_len(p2.lnum);
- if (p2.col > 0)
- {
- p2.col--;
-
- mb_adjustpos(curbuf, &p2);
- }
- }
- }
- // if fp2 is on NUL (empty line) inclusive becomes false
- if (*ml_get_pos(&p2) == NUL && !virtual_op)
- inclusive = FALSE;
+ // Handle 'selection' == "exclusive".
+ if (is_select_exclusive && !EQUAL_POS(*p1, *p2))
+ // When backing up to previous line, inclusive becomes false.
+ *inclusive = !unadjust_for_sel_inner(p2);
+ // If p2 is on NUL (end of line), inclusive becomes false.
+ if (*inclusive && !virtual_op && *ml_get_pos(p2) == NUL)
+ *inclusive = FALSE;
}
- else if (region_type == MBLOCK)
+ else if (*region_type == MBLOCK)
{
colnr_T sc1, ec1, sc2, ec2;
- getvvcol(curwin, &p1, &sc1, NULL, &ec1);
- getvvcol(curwin, &p2, &sc2, NULL, &ec2);
- oa.motion_type = MBLOCK;
- oa.inclusive = TRUE;
- oa.op_type = OP_NOP;
- oa.start = p1;
- oa.end = p2;
- oa.start_vcol = MIN(sc1, sc2);
- if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1)
- oa.end_vcol = sc2 - 1;
+ getvvcol(curwin, p1, &sc1, NULL, &ec1);
+ getvvcol(curwin, p2, &sc2, NULL, &ec2);
+ oap->motion_type = MBLOCK;
+ oap->inclusive = TRUE;
+ oap->op_type = OP_NOP;
+ oap->start = *p1;
+ oap->end = *p2;
+ oap->start_vcol = MIN(sc1, sc2);
+ if (block_width > 0)
+ oap->end_vcol = oap->start_vcol + block_width - 1;
+ else if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1)
+ oap->end_vcol = sc2 - 1;
else
- oa.end_vcol = MAX(ec1, ec2);
+ oap->end_vcol = MAX(ec1, ec2);
}
// Include the trailing byte of a multi-byte char.
- l = utfc_ptr2len((char_u *)ml_get_pos(&p2));
+ l = mb_ptr2len((char_u *)ml_get_pos(p2));
if (l > 1)
- p2.col += l - 1;
+ p2->col += l - 1;
+
+ return OK;
+}
+
+/*
+ * "getregion()" function
+ */
+ static void
+f_getregion(typval_T *argvars, typval_T *rettv)
+{
+ pos_T p1, p2;
+ int inclusive = TRUE;
+ int region_type = -1;
+ oparg_T oa;
+
+ buf_T *save_curbuf;
+ int save_virtual;
+ char_u *akt = NULL;
+ linenr_T lnum;
+
+ save_curbuf = curbuf;
+ save_virtual = virtual_op;
+
+ if (getregionpos(argvars, rettv,
+ &p1, &p2, &inclusive, &region_type, &oa) == FAIL)
+ return;
for (lnum = p1.lnum; lnum <= p2.lnum; lnum++)
{
int ret = 0;
+ struct block_def bd;
if (region_type == MLINE)
akt = vim_strsave(ml_get(lnum));
@@ -5675,6 +5699,187 @@ f_getregion(typval_T *argvars, typval_T *rettv)
}
}
+ // getregionpos() may change curbuf and virtual_op
+ curbuf = save_curbuf;
+ curwin->w_buffer = curbuf;
+ virtual_op = save_virtual;
+}
+
+ static void
+add_regionpos_range(typval_T *rettv, pos_T p1, pos_T p2)
+{
+ list_T *l1, *l2, *l3;
+
+ l1 = list_alloc();
+ if (l1 == NULL)
+ return;
+
+ if (list_append_list(rettv->vval.v_list, l1) == FAIL)
+ {
+ vim_free(l1);
+ return;
+ }
+
+ l2 = list_alloc();
+ if (l2 == NULL)
+ {
+ vim_free(l1);
+ return;
+ }
+
+ if (list_append_list(l1, l2) == FAIL)
+ {
+ vim_free(l1);
+ vim_free(l2);
+ return;
+ }
+
+ l3 = list_alloc();
+ if (l3 == NULL)
+ {
+ vim_free(l1);
+ vim_free(l2);
+ return;
+ }
+
+ if (list_append_list(l1, l3) == FAIL)
+ {
+ vim_free(l1);
+ vim_free(l2);
+ vim_free(l3);
+ return;
+ }
+
+ list_append_number(l2, curbuf->b_fnum);
+ list_append_number(l2, p1.lnum);
+ list_append_number(l2, p1.col);
+ list_append_number(l2, p1.coladd);
+
+ list_append_number(l3, curbuf->b_fnum);
+ list_append_number(l3, p2.lnum);
+ list_append_number(l3, p2.col);
+ list_append_number(l3, p2.coladd);
+}
+
+/*
+ * "getregionpos()" function
+ */
+ static void
+f_getregionpos(typval_T *argvars, typval_T *rettv)
+{
+ pos_T p1, p2;
+ int inclusive = TRUE;
+ int region_type = -1;
+ int allow_eol = FALSE;
+ oparg_T oa;
+ int lnum;
+
+ buf_T *save_curbuf;
+ int save_virtual;
+
+ save_curbuf = curbuf;
+ save_virtual = virtual_op;
+
+ if (getregionpos(argvars, rettv,
+ &p1, &p2, &inclusive, &region_type, &oa) == FAIL)
+ return;
+
+ if (argvars[2].v_type == VAR_DICT)
+ allow_eol = dict_get_bool(argvars[2].vval.v_dict, "eol", FALSE);
+
+ for (lnum = p1.lnum; lnum <= p2.lnum; lnum++)
+ {
+ pos_T ret_p1, ret_p2;
+ char_u *line = ml_get(lnum);
+ colnr_T line_len = ml_get_len(lnum);
+
+ if (region_type == MLINE)
+ {
+ ret_p1.col = 1;
+ ret_p1.coladd = 0;
+ ret_p2.col = MAXCOL;
+ ret_p2.coladd = 0;
+ }
+ else
+ {
+ struct block_def bd;
+
+ if (region_type == MBLOCK)
+ block_prep(&oa, &bd, lnum, FALSE);
+ else
+ charwise_block_prep(p1, p2, &bd, lnum, inclusive);
+
+ if (bd.is_oneChar) // selection entirely inside one char
+ {
+ if (region_type == MBLOCK)
+ {
+ ret_p1.col = mb_prevptr(line, bd.textstart) - line + 1;
+ ret_p1.coladd = bd.start_char_vcols
+ - (bd.start_vcol - oa.start_vcol);
+ }
+ else
+ {
+ ret_p1.col = p1.col + 1;
+ ret_p1.coladd = p1.coladd;
+ }
+ }
+ else if (region_type == MBLOCK && oa.start_vcol > bd.start_vcol)
+ {
+ // blockwise selection entirely beyond end of line
+ ret_p1.col = MAXCOL;
+ ret_p1.coladd = oa.start_vcol - bd.start_vcol;
+ bd.is_oneChar = TRUE;
+ }
+ else if (bd.startspaces > 0)
+ {
+ ret_p1.col = mb_prevptr(line, bd.textstart) - line + 1;
+ ret_p1.coladd = bd.start_char_vcols - bd.startspaces;
+ }
+ else
+ {
+ ret_p1.col = bd.textcol + 1;
+ ret_p1.coladd = 0;
+ }
+
+ if (bd.is_oneChar) // selection entirely inside one char
+ {
+ ret_p2.col = ret_p1.col;
+ ret_p2.coladd = ret_p1.coladd + bd.startspaces + bd.endspaces;
+ }
+ else if (bd.endspaces > 0)
+ {
+ ret_p2.col = bd.textcol + bd.textlen + 1;
+ ret_p2.coladd = bd.endspaces;
+ }
+ else
+ {
+ ret_p2.col = bd.textcol + bd.textlen;
+ ret_p2.coladd = 0;
+ }
+ }
+
+ if (!allow_eol && ret_p1.col > line_len)
+ {
+ ret_p1.col = 0;
+ ret_p1.coladd = 0;
+ }
+ else if (ret_p1.col > line_len + 1)
+ ret_p1.col = line_len + 1;
+
+ if (!allow_eol && ret_p2.col > line_len)
+ {
+ ret_p2.col = ret_p1.col == 0 ? 0 : line_len;
+ ret_p2.coladd = 0;
+ }
+ else if (ret_p2.col > line_len + 1)
+ ret_p2.col = line_len + 1;
+
+ ret_p1.lnum = lnum;
+ ret_p2.lnum = lnum;
+ add_regionpos_range(rettv, ret_p1, ret_p2);
+ }
+
+ // getregionpos() may change curbuf and virtual_op
curbuf = save_curbuf;
curwin->w_buffer = curbuf;
virtual_op = save_virtual;
@@ -7368,6 +7573,7 @@ indexof_blob(blob_T *b, long startidx, typval_T *expr)
set_vim_var_type(VV_KEY, VAR_NUMBER);
set_vim_var_type(VV_VAL, VAR_NUMBER);
+ int called_emsg_start = called_emsg;
for (idx = startidx; idx < blob_len(b); ++idx)
{
set_vim_var_nr(VV_KEY, idx);
@@ -7375,6 +7581,9 @@ indexof_blob(blob_T *b, long startidx, typval_T *expr)
if (indexof_eval_expr(expr))
return idx;
+
+ if (called_emsg != called_emsg_start)
+ return -1;
}
return -1;
@@ -7410,6 +7619,7 @@ indexof_list(list_T *l, long startidx, typval_T *expr)
set_vim_var_type(VV_KEY, VAR_NUMBER);
+ int called_emsg_start = called_emsg;
for ( ; item != NULL; item = item->li_next, ++idx)
{
set_vim_var_nr(VV_KEY, idx);
@@ -7420,6 +7630,9 @@ indexof_list(list_T *l, long startidx, typval_T *expr)
if (found)
return idx;
+
+ if (called_emsg != called_emsg_start)
+ return -1;
}
return -1;
@@ -7443,7 +7656,9 @@ f_indexof(typval_T *argvars, typval_T *rettv)
|| check_for_opt_dict_arg(argvars, 2) == FAIL)
return;
- if ((argvars[1].v_type == VAR_STRING && argvars[1].vval.v_string == NULL)
+ if ((argvars[1].v_type == VAR_STRING &&
+ (argvars[1].vval.v_string == NULL
+ || *argvars[1].vval.v_string == NUL))
|| (argvars[1].v_type == VAR_FUNC
&& argvars[1].vval.v_partial == NULL))
return;
@@ -9540,6 +9755,7 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
{
int flags;
char_u *pat;
+ size_t patlen;
pos_T pos;
pos_T save_cursor;
int save_p_ws = p_ws;
@@ -9614,10 +9830,12 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp)
sia.sa_tm = time_limit;
#endif
+ patlen = STRLEN(pat);
+
// Repeat until {skip} returns FALSE.
for (;;)
{
- subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L,
+ subpatnum = searchit(curwin, curbuf, &pos, NULL, dir, pat, patlen, 1L,
options, RE_SEARCH, &sia);
// finding the first match again means there is no match where {skip}
// evaluates to zero.
@@ -10030,6 +10248,13 @@ do_searchpair(
{
char_u *save_cpo;
char_u *pat, *pat2 = NULL, *pat3 = NULL;
+ size_t patlen;
+ size_t spatlen;
+ size_t epatlen;
+ size_t pat2size;
+ size_t pat2len;
+ size_t pat3size;
+ size_t pat3len;
long retval = 0;
pos_T pos;
pos_T firstpos;
@@ -10049,15 +10274,24 @@ do_searchpair(
// Make two search patterns: start/end (pat2, for in nested pairs) and
// start/middle/end (pat3, for the top pair).
- pat2 = alloc(STRLEN(spat) + STRLEN(epat) + 17);
- pat3 = alloc(STRLEN(spat) + STRLEN(mpat) + STRLEN(epat) + 25);
- if (pat2 == NULL || pat3 == NULL)
+ spatlen = STRLEN(spat);
+ epatlen = STRLEN(epat);
+ pat2size = spatlen + epatlen + 17;
+ pat2 = alloc(pat2size);
+ if (pat2 == NULL)
+ goto theend;
+ pat3size = spatlen + STRLEN(mpat) + epatlen + 25;
+ pat3 = alloc(pat3size);
+ if (pat3 == NULL)
goto theend;
- sprintf((char *)pat2, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
+ pat2len = vim_snprintf((char *)pat2, pat2size, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)", spat, epat);
if (*mpat == NUL)
+ {
STRCPY(pat3, pat2);
+ pat3len = pat2len;
+ }
else
- sprintf((char *)pat3, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)",
+ pat3len = vim_snprintf((char *)pat3, pat3size, "\\m\\(%s\\m\\)\\|\\(%s\\m\\)\\|\\(%s\\m\\)",
spat, epat, mpat);
if (flags & SP_START)
options |= SEARCH_START;
@@ -10074,13 +10308,14 @@ do_searchpair(
CLEAR_POS(&firstpos);
CLEAR_POS(&foundpos);
pat = pat3;
+ patlen = pat3len;
for (;;)
{
searchit_arg_T sia;
CLEAR_FIELD(sia);
sia.sa_stop_lnum = lnum_stop;
- n = searchit(curwin, curbuf, &pos, NULL, dir, pat, 1L,
+ n = searchit(curwin, curbuf, &pos, NULL, dir, pat, patlen, 1L,
options, RE_SEARCH, &sia);
if (n == FAIL || (firstpos.lnum != 0 && EQUAL_POS(pos, firstpos)))
// didn't find it or found the first match again: FAIL
@@ -10715,7 +10950,7 @@ f_shiftwidth(typval_T *argvars UNUSED, typval_T *rettv)
if (col < 0)
return; // type error; errmsg already given
#ifdef FEAT_VARTABS
- rettv->vval.v_number = get_sw_value_col(curbuf, col);
+ rettv->vval.v_number = get_sw_value_col(curbuf, col, FALSE);
return;
#endif
}
@@ -10784,7 +11019,7 @@ f_spellbadword(typval_T *argvars UNUSED, typval_T *rettv)
if (argvars[0].v_type == VAR_UNKNOWN)
{
// Find the start and length of the badly spelled word.
- len = spell_move_to(curwin, FORWARD, TRUE, TRUE, &attr);
+ len = spell_move_to(curwin, FORWARD, SMT_ALL, TRUE, &attr);
if (len != 0)
{
word = ml_get_cursor();
@@ -11497,7 +11732,7 @@ f_type(typval_T *argvars, typval_T *rettv)
case VAR_CLASS:
{
class_T *cl = argvars[0].vval.v_class;
- if (IS_ENUM(cl))
+ if (cl != NULL && IS_ENUM(cl))
n = VAR_TYPE_ENUM;
else
n = VAR_TYPE_CLASS;
@@ -11505,11 +11740,18 @@ f_type(typval_T *argvars, typval_T *rettv)
}
case VAR_OBJECT:
{
- class_T *cl = argvars[0].vval.v_object->obj_class;
- if (IS_ENUM(cl))
- n = VAR_TYPE_ENUMVALUE;
- else
+ object_T *obj = argvars[0].vval.v_object;
+
+ if (obj == NULL)
n = VAR_TYPE_OBJECT;
+ else
+ {
+ class_T *cl = argvars[0].vval.v_object->obj_class;
+ if (IS_ENUM(cl))
+ n = VAR_TYPE_ENUMVALUE;
+ else
+ n = VAR_TYPE_OBJECT;
+ }
break;
}
case VAR_UNKNOWN:
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 2a5d842..8143c24 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3750,6 +3750,7 @@ ex_substitute(exarg_T *eap)
int save_do_all; // remember user specified 'g' flag
int save_do_ask; // remember user specified 'c' flag
char_u *pat = NULL, *sub = NULL; // init for GCC
+ size_t patlen = 0;
int delimiter;
int sublen;
int got_quit = FALSE;
@@ -3823,6 +3824,7 @@ ex_substitute(exarg_T *eap)
if (*cmd != '&')
which_pat = RE_SEARCH; // use last '/' pattern
pat = (char_u *)""; // empty search pattern
+ patlen = 0;
delimiter = *cmd++; // remember delimiter character
}
else // find the end of the regexp
@@ -3830,6 +3832,7 @@ ex_substitute(exarg_T *eap)
which_pat = RE_LAST; // use last used regexp
delimiter = *cmd++; // remember delimiter character
pat = cmd; // remember start of search pat
+ patlen = STRLEN(pat);
cmd = skip_regexp_ex(cmd, delimiter, magic_isset(),
&eap->arg, NULL, NULL);
if (cmd[0] == delimiter) // end delimiter found
@@ -3883,6 +3886,7 @@ ex_substitute(exarg_T *eap)
return;
}
pat = NULL; // search_regcomp() will use previous pattern
+ patlen = 0;
sub = vim_strsave(old_sub);
// Vi compatibility quirk: repeating with ":s" keeps the cursor in the
@@ -3929,9 +3933,9 @@ ex_substitute(exarg_T *eap)
}
if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0)
- save_re_pat(RE_SUBST, pat, magic_isset());
+ save_re_pat(RE_SUBST, pat, patlen, magic_isset());
// put pattern in history
- add_to_history(HIST_SEARCH, pat, TRUE, NUL);
+ add_to_history(HIST_SEARCH, pat, patlen, TRUE, NUL);
vim_free(sub);
return;
@@ -4066,7 +4070,7 @@ ex_substitute(exarg_T *eap)
return;
}
- if (search_regcomp(pat, NULL, RE_SUBST, which_pat, SEARCH_HIS, &regmatch) == FAIL)
+ if (search_regcomp(pat, patlen, NULL, RE_SUBST, which_pat, SEARCH_HIS, &regmatch) == FAIL)
{
if (subflags.do_error)
emsg(_(e_invalid_command));
@@ -5104,6 +5108,7 @@ ex_global(exarg_T *eap)
char_u delim; // delimiter, normally '/'
char_u *pat;
+ size_t patlen;
char_u *used_pat;
regmmatch_T regmatch;
int match;
@@ -5150,6 +5155,7 @@ ex_global(exarg_T *eap)
which_pat = RE_SEARCH; // use previous search pattern
++cmd;
pat = (char_u *)"";
+ patlen = 0;
}
else if (*cmd == NUL)
{
@@ -5165,12 +5171,13 @@ ex_global(exarg_T *eap)
delim = *cmd; // get the delimiter
++cmd; // skip delimiter if there is one
pat = cmd; // remember start of pattern
+ patlen = STRLEN(pat);
cmd = skip_regexp_ex(cmd, delim, magic_isset(), &eap->arg, NULL, NULL);
if (cmd[0] == delim) // end delimiter found
*cmd++ = NUL; // replace it with a NUL
}
- if (search_regcomp(pat, &used_pat, RE_BOTH, which_pat, SEARCH_HIS,
+ if (search_regcomp(pat, patlen, &used_pat, RE_BOTH, which_pat, SEARCH_HIS,
&regmatch) == FAIL)
{
emsg(_(e_invalid_command));
@@ -5622,6 +5629,9 @@ ex_oldfiles(exarg_T *eap UNUSED)
listitem_T *li;
int nr = 0;
char_u *fname;
+ // for a single filtered match, remember the number
+ // so we can jump directly to it without prompting
+ int matches = -1;
if (l == NULL)
{
@@ -5637,6 +5647,10 @@ ex_oldfiles(exarg_T *eap UNUSED)
fname = tv_get_string(&li->li_tv);
if (!message_filtered(fname))
{
+ if (matches < 0)
+ matches = nr;
+ else
+ matches = 0;
msg_outnum((long)nr);
msg_puts(": ");
msg_outtrans(fname);
@@ -5654,7 +5668,15 @@ ex_oldfiles(exarg_T *eap UNUSED)
if (cmdmod.cmod_flags & CMOD_BROWSE)
{
quit_more = FALSE;
- nr = prompt_for_number(FALSE);
+ // we only need to prompt if there is more than 1 match
+ if (matches > 0)
+ {
+ nr = matches;
+ // msg_putchar above sets needs_wait_return
+ need_wait_return = FALSE;
+ }
+ else
+ nr = prompt_for_number(FALSE);
msg_starthere();
if (nr > 0)
{
diff --git a/src/ex_cmds.h b/src/ex_cmds.h
index 40dec4c..25f6914 100644
--- a/src/ex_cmds.h
+++ b/src/ex_cmds.h
@@ -271,7 +271,7 @@ EXCMD(CMD_cabove, "cabove", ex_cbelow,
ADDR_UNSIGNED),
EXCMD(CMD_caddbuffer, "caddbuffer", ex_cbuffer,
EX_RANGE|EX_WORD1|EX_TRLBAR,
- ADDR_OTHER),
+ ADDR_LINES),
EXCMD(CMD_caddexpr, "caddexpr", ex_cexpr,
EX_NEEDARG|EX_WORD1|EX_NOTRLCOM|EX_EXPR_ARG,
ADDR_NONE),
@@ -289,7 +289,7 @@ EXCMD(CMD_catch, "catch", ex_catch,
ADDR_NONE),
EXCMD(CMD_cbuffer, "cbuffer", ex_cbuffer,
EX_BANG|EX_RANGE|EX_WORD1|EX_TRLBAR,
- ADDR_OTHER),
+ ADDR_LINES),
EXCMD(CMD_cbefore, "cbefore", ex_cbelow,
EX_RANGE|EX_COUNT|EX_TRLBAR,
ADDR_UNSIGNED),
@@ -331,7 +331,7 @@ EXCMD(CMD_cgetfile, "cgetfile", ex_cfile,
ADDR_NONE),
EXCMD(CMD_cgetbuffer, "cgetbuffer", ex_cbuffer,
EX_RANGE|EX_WORD1|EX_TRLBAR,
- ADDR_OTHER),
+ ADDR_LINES),
EXCMD(CMD_cgetexpr, "cgetexpr", ex_cexpr,
EX_NEEDARG|EX_WORD1|EX_NOTRLCOM|EX_EXPR_ARG,
ADDR_NONE),
@@ -820,7 +820,7 @@ EXCMD(CMD_laddexpr, "laddexpr", ex_cexpr,
ADDR_NONE),
EXCMD(CMD_laddbuffer, "laddbuffer", ex_cbuffer,
EX_RANGE|EX_WORD1|EX_TRLBAR,
- ADDR_OTHER),
+ ADDR_LINES),
EXCMD(CMD_laddfile, "laddfile", ex_cfile,
EX_TRLBAR|EX_FILE1,
ADDR_NONE),
@@ -832,7 +832,7 @@ EXCMD(CMD_later, "later", ex_later,
ADDR_NONE),
EXCMD(CMD_lbuffer, "lbuffer", ex_cbuffer,
EX_BANG|EX_RANGE|EX_WORD1|EX_TRLBAR,
- ADDR_OTHER),
+ ADDR_LINES),
EXCMD(CMD_lbefore, "lbefore", ex_cbelow,
EX_RANGE|EX_COUNT|EX_TRLBAR,
ADDR_UNSIGNED),
@@ -886,7 +886,7 @@ EXCMD(CMD_lgetfile, "lgetfile", ex_cfile,
ADDR_NONE),
EXCMD(CMD_lgetbuffer, "lgetbuffer", ex_cbuffer,
EX_RANGE|EX_WORD1|EX_TRLBAR,
- ADDR_OTHER),
+ ADDR_LINES),
EXCMD(CMD_lgetexpr, "lgetexpr", ex_cexpr,
EX_NEEDARG|EX_WORD1|EX_NOTRLCOM|EX_EXPR_ARG,
ADDR_NONE),
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index a588f26..71bfa93 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -4567,7 +4567,7 @@ get_address(
curwin->w_cursor.col = 0;
searchcmdlen = 0;
flags = silent ? 0 : SEARCH_HIS | SEARCH_MSG;
- if (!do_search(NULL, c, c, cmd, 1L, flags, NULL))
+ if (!do_search(NULL, c, c, cmd, STRLEN(cmd), 1L, flags, NULL))
{
curwin->w_cursor = pos;
cmd = NULL;
@@ -4621,7 +4621,7 @@ get_address(
pos.coladd = 0;
if (searchit(curwin, curbuf, &pos, NULL,
*cmd == '?' ? BACKWARD : FORWARD,
- (char_u *)"", 1L, SEARCH_MSG, i, NULL) != FAIL)
+ (char_u *)"", 0, 1L, SEARCH_MSG, i, NULL) != FAIL)
lnum = pos.lnum;
else
{
@@ -5077,7 +5077,7 @@ expand_filename(
{
int has_wildcards; // need to expand wildcards
char_u *repl;
- int srclen;
+ size_t srclen;
char_u *p;
int n;
int escaped;
@@ -5201,7 +5201,7 @@ expand_filename(
}
}
- p = repl_cmdline(eap, p, (size_t)srclen, repl, cmdlinep);
+ p = repl_cmdline(eap, p, srclen, repl, cmdlinep);
vim_free(repl);
if (p == NULL)
return FAIL;
@@ -9363,7 +9363,7 @@ enum {
* the variable. Otherwise return -1 and "*usedlen" is unchanged.
*/
int
-find_cmdline_var(char_u *src, int *usedlen)
+find_cmdline_var(char_u *src, size_t *usedlen)
{
// must be sorted by the 'value' field because it is used by bsearch()!
static keyvalue_T spec_str_tab[] = {
@@ -9444,7 +9444,7 @@ find_cmdline_var(char_u *src, int *usedlen)
eval_vars(
char_u *src, // pointer into commandline
char_u *srcstart, // beginning of valid memory for src
- int *usedlen, // characters after src that are used
+ size_t *usedlen, // characters after src that are used
linenr_T *lnump, // line number for :e command, or NULL
char **errormsg, // pointer to error message
int *escaped, // return value has escaped white space (can
@@ -9514,7 +9514,7 @@ eval_vars(
*/
else
{
- int off = 0;
+ size_t off = 0;
switch (spec_idx)
{
@@ -9781,7 +9781,7 @@ expand_sfile(char_u *arg)
size_t len;
char_u *repl;
size_t repllen;
- int srclen;
+ size_t srclen;
char_u *p;
resultlen = STRLEN(arg);
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 1731d29..3ae4958 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -493,7 +493,7 @@ may_do_incsearch_highlighting(
sia.sa_tm = 500;
#endif
found = do_search(NULL, firstc == ':' ? '/' : firstc, search_delim,
- ccline.cmdbuff + skiplen, count, search_flags,
+ ccline.cmdbuff + skiplen, patlen, count, search_flags,
#ifdef FEAT_RELTIME
&sia
#else
@@ -654,7 +654,7 @@ may_adjust_incsearch_highlighting(
pat[patlen] = NUL;
i = searchit(curwin, curbuf, &t, NULL,
c == Ctrl_G ? FORWARD : BACKWARD,
- pat, count, search_flags, RE_SEARCH, NULL);
+ pat, patlen, count, search_flags, RE_SEARCH, NULL);
--emsg_off;
pat[patlen] = save;
if (i)
@@ -2539,12 +2539,14 @@ returncmd:
if (ccline.cmdlen && firstc != NUL
&& (some_key_typed || histype == HIST_SEARCH))
{
- add_to_history(histype, ccline.cmdbuff, TRUE,
+ size_t cmdbufflen = STRLEN(ccline.cmdbuff);
+
+ add_to_history(histype, ccline.cmdbuff, cmdbufflen, TRUE,
histype == HIST_SEARCH ? firstc : NUL);
if (firstc == ':')
{
vim_free(new_last_cmdline);
- new_last_cmdline = vim_strsave(ccline.cmdbuff);
+ new_last_cmdline = vim_strnsave(ccline.cmdbuff, cmdbufflen);
}
}
diff --git a/src/fileio.c b/src/fileio.c
index 07e05fc..e7f3332 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -3770,19 +3770,12 @@ vim_fgets(char_u *buf, int size, FILE *fp)
int
vim_rename(char_u *from, char_u *to)
{
- int fd_in;
- int fd_out;
int n;
- char *errmsg = NULL;
- char *buffer;
+ int ret;
#ifdef AMIGA
BPTR flock;
#endif
stat_T st;
- long perm;
-#ifdef HAVE_ACL
- vim_acl_T acl; // ACL from original file
-#endif
int use_tmp_file = FALSE;
/*
@@ -3903,6 +3896,61 @@ vim_rename(char_u *from, char_u *to)
/*
* Rename() failed, try copying the file.
*/
+ ret = vim_copyfile(from, to);
+ if (ret != OK)
+ return -1;
+
+ /*
+ * Remove copied original file
+ */
+ if (mch_stat((char *)from, &st) >= 0)
+ mch_remove(from);
+
+ return 0;
+}
+
+
+/*
+ * Create the new file with same permissions as the original.
+ * Return -1 for failure, 0 for success.
+ */
+ int
+vim_copyfile(char_u *from, char_u *to)
+{
+ int fd_in;
+ int fd_out;
+ int n;
+ char *errmsg = NULL;
+ char *buffer;
+ long perm;
+#ifdef HAVE_ACL
+ vim_acl_T acl; // ACL from original file
+#endif
+
+#ifdef HAVE_READLINK
+ int ret;
+ int len;
+ stat_T st;
+ char linkbuf[MAXPATHL + 1];
+
+ ret = mch_lstat((char *)from, &st);
+ if (ret >= 0 && S_ISLNK(st.st_mode))
+ {
+ ret = FAIL;
+
+ len = readlink((char *)from, linkbuf, MAXPATHL);
+ if (len > 0)
+ {
+ linkbuf[len] = NUL;
+
+ // Create link
+ ret = symlink(linkbuf, (char *)to);
+ }
+
+ return ret == 0 ? OK : FAIL;
+ }
+#endif
+
perm = mch_getperm(from);
#ifdef HAVE_ACL
// For systems that support ACL: get the ACL from the original file.
@@ -3914,7 +3962,7 @@ vim_rename(char_u *from, char_u *to)
#ifdef HAVE_ACL
mch_free_acl(acl);
#endif
- return -1;
+ return FAIL;
}
// Create the new file with same permissions as the original.
@@ -3926,7 +3974,7 @@ vim_rename(char_u *from, char_u *to)
#ifdef HAVE_ACL
mch_free_acl(acl);
#endif
- return -1;
+ return FAIL;
}
buffer = alloc(WRITEBUFSIZE);
@@ -3937,7 +3985,7 @@ vim_rename(char_u *from, char_u *to)
#ifdef HAVE_ACL
mch_free_acl(acl);
#endif
- return -1;
+ return FAIL;
}
while ((n = read_eintr(fd_in, buffer, WRITEBUFSIZE)) > 0)
@@ -3969,10 +4017,9 @@ vim_rename(char_u *from, char_u *to)
if (errmsg != NULL)
{
semsg(errmsg, to);
- return -1;
+ return FAIL;
}
- mch_remove(from);
- return 0;
+ return OK;
}
static int already_warned = FALSE;
diff --git a/src/filepath.c b/src/filepath.c
index 3bf8a2d..9f68d7c 100644
--- a/src/filepath.c
+++ b/src/filepath.c
@@ -292,7 +292,7 @@ shortpath_for_partial(
modify_fname(
char_u *src, // string with modifiers
int tilde_file, // "~" is a file name, not $HOME
- int *usedlen, // characters after src that are used
+ size_t *usedlen, // characters after src that are used
char_u **fnamep, // file name so far
char_u **bufp, // buffer for allocated file name or NULL
int *fnamelen) // length of fnamep
@@ -668,7 +668,7 @@ repeat:
str = vim_strnsave(*fnamep, *fnamelen);
if (sub != NULL && str != NULL)
{
- *usedlen = (int)(p + 1 - src);
+ *usedlen = p + 1 - src;
s = do_string_sub(str, pat, sub, NULL, flags);
if (s != NULL)
{
@@ -1038,7 +1038,7 @@ f_fnamemodify(typval_T *argvars, typval_T *rettv)
{
char_u *fname;
char_u *mods;
- int usedlen = 0;
+ size_t usedlen = 0;
int len = 0;
char_u *fbuf = NULL;
char_u buf[NUMBUFLEN];
@@ -2649,6 +2649,31 @@ f_browsedir(typval_T *argvars UNUSED, typval_T *rettv)
rettv->v_type = VAR_STRING;
}
+/*
+ * "filecopy()" function
+ */
+ void
+f_filecopy(typval_T *argvars, typval_T *rettv)
+{
+ char_u *from;
+ stat_T st;
+
+ rettv->vval.v_number = FALSE;
+
+ if (check_restricted() || check_secure()
+ || check_for_string_arg(argvars, 0) == FAIL
+ || check_for_string_arg(argvars, 1) == FAIL)
+ return;
+
+ from = tv_get_string(&argvars[0]);
+
+ if (mch_lstat((char *)from, &st) >= 0
+ && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
+ rettv->vval.v_number = vim_copyfile(
+ tv_get_string(&argvars[0]),
+ tv_get_string(&argvars[1])) == OK ? TRUE : FALSE;
+}
+
#endif // FEAT_EVAL
/*
@@ -2707,7 +2732,7 @@ home_replace(
if (homedir_env != NULL && *homedir_env == '~')
{
- int usedlen = 0;
+ size_t usedlen = 0;
int flen;
char_u *fbuf = NULL;
@@ -3170,7 +3195,7 @@ expand_wildcards_eval(
char_u *eval_pat = NULL;
char_u *exp_pat = *pat;
char *ignored_msg;
- int usedlen;
+ size_t usedlen;
int is_cur_alt_file = *exp_pat == '%' || *exp_pat == '#';
int star_follows = FALSE;
diff --git a/src/gc.c b/src/gc.c
new file mode 100644
index 0000000..987ca27
--- /dev/null
+++ b/src/gc.c
@@ -0,0 +1,780 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * gc.c: Garbage Collection
+ */
+
+#include "vim.h"
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+
+/*
+ * When recursively copying lists and dicts we need to remember which ones we
+ * have done to avoid endless recursiveness. This unique ID is used for that.
+ * The last bit is used for previous_funccal, ignored when comparing.
+ */
+static int current_copyID = 0;
+
+static int free_unref_items(int copyID);
+
+/*
+ * Return the next (unique) copy ID.
+ * Used for serializing nested structures.
+ */
+ int
+get_copyID(void)
+{
+ current_copyID += COPYID_INC;
+ return current_copyID;
+}
+
+/*
+ * Garbage collection for lists and dictionaries.
+ *
+ * We use reference counts to be able to free most items right away when they
+ * are no longer used. But for composite items it's possible that it becomes
+ * unused while the reference count is > 0: When there is a recursive
+ * reference. Example:
+ * :let l = [1, 2, 3]
+ * :let d = {9: l}
+ * :let l[1] = d
+ *
+ * Since this is quite unusual we handle this with garbage collection: every
+ * once in a while find out which lists and dicts are not referenced from any
+ * variable.
+ *
+ * Here is a good reference text about garbage collection (refers to Python
+ * but it applies to all reference-counting mechanisms):
+ * http://python.ca/nas/python/gc/
+ */
+
+/*
+ * Do garbage collection for lists and dicts.
+ * When "testing" is TRUE this is called from test_garbagecollect_now().
+ * Return TRUE if some memory was freed.
+ */
+ int
+garbage_collect(int testing)
+{
+ int copyID;
+ int abort = FALSE;
+ buf_T *buf;
+ win_T *wp;
+ int did_free = FALSE;
+ tabpage_T *tp;
+
+ if (!testing)
+ {
+ // Only do this once.
+ want_garbage_collect = FALSE;
+ may_garbage_collect = FALSE;
+ garbage_collect_at_exit = FALSE;
+ }
+
+ // The execution stack can grow big, limit the size.
+ if (exestack.ga_maxlen - exestack.ga_len > 500)
+ {
+ size_t new_len;
+ char_u *pp;
+ int n;
+
+ // Keep 150% of the current size, with a minimum of the growth size.
+ n = exestack.ga_len / 2;
+ if (n < exestack.ga_growsize)
+ n = exestack.ga_growsize;
+
+ // Don't make it bigger though.
+ if (exestack.ga_len + n < exestack.ga_maxlen)
+ {
+ new_len = (size_t)exestack.ga_itemsize * (exestack.ga_len + n);
+ pp = vim_realloc(exestack.ga_data, new_len);
+ if (pp == NULL)
+ return FAIL;
+ exestack.ga_maxlen = exestack.ga_len + n;
+ exestack.ga_data = pp;
+ }
+ }
+
+ // We advance by two because we add one for items referenced through
+ // previous_funccal.
+ copyID = get_copyID();
+
+ /*
+ * 1. Go through all accessible variables and mark all lists and dicts
+ * with copyID.
+ */
+
+ // Don't free variables in the previous_funccal list unless they are only
+ // referenced through previous_funccal. This must be first, because if
+ // the item is referenced elsewhere the funccal must not be freed.
+ abort = abort || set_ref_in_previous_funccal(copyID);
+
+ // script-local variables
+ abort = abort || garbage_collect_scriptvars(copyID);
+
+ // buffer-local variables
+ FOR_ALL_BUFFERS(buf)
+ abort = abort || set_ref_in_item(&buf->b_bufvar.di_tv, copyID,
+ NULL, NULL);
+
+ // window-local variables
+ FOR_ALL_TAB_WINDOWS(tp, wp)
+ abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
+ NULL, NULL);
+ // window-local variables in autocmd windows
+ for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
+ if (aucmd_win[i].auc_win != NULL)
+ abort = abort || set_ref_in_item(
+ &aucmd_win[i].auc_win->w_winvar.di_tv, copyID, NULL, NULL);
+#ifdef FEAT_PROP_POPUP
+ FOR_ALL_POPUPWINS(wp)
+ abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
+ NULL, NULL);
+ FOR_ALL_TABPAGES(tp)
+ FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
+ abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
+ NULL, NULL);
+#endif
+
+ // tabpage-local variables
+ FOR_ALL_TABPAGES(tp)
+ abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID,
+ NULL, NULL);
+ // global variables
+ abort = abort || garbage_collect_globvars(copyID);
+
+ // function-local variables
+ abort = abort || set_ref_in_call_stack(copyID);
+
+ // named functions (matters for closures)
+ abort = abort || set_ref_in_functions(copyID);
+
+ // function call arguments, if v:testing is set.
+ abort = abort || set_ref_in_func_args(copyID);
+
+ // funcstacks keep variables for closures
+ abort = abort || set_ref_in_funcstacks(copyID);
+
+ // loopvars keep variables for loop blocks
+ abort = abort || set_ref_in_loopvars(copyID);
+
+ // v: vars
+ abort = abort || garbage_collect_vimvars(copyID);
+
+ // callbacks in buffers
+ abort = abort || set_ref_in_buffers(copyID);
+
+ // 'completefunc', 'omnifunc' and 'thesaurusfunc' callbacks
+ abort = abort || set_ref_in_insexpand_funcs(copyID);
+
+ // 'operatorfunc' callback
+ abort = abort || set_ref_in_opfunc(copyID);
+
+ // 'tagfunc' callback
+ abort = abort || set_ref_in_tagfunc(copyID);
+
+ // 'imactivatefunc' and 'imstatusfunc' callbacks
+ abort = abort || set_ref_in_im_funcs(copyID);
+
+#ifdef FEAT_LUA
+ abort = abort || set_ref_in_lua(copyID);
+#endif
+
+#ifdef FEAT_PYTHON
+ abort = abort || set_ref_in_python(copyID);
+#endif
+
+#ifdef FEAT_PYTHON3
+ abort = abort || set_ref_in_python3(copyID);
+#endif
+
+#ifdef FEAT_JOB_CHANNEL
+ abort = abort || set_ref_in_channel(copyID);
+ abort = abort || set_ref_in_job(copyID);
+#endif
+#ifdef FEAT_NETBEANS_INTG
+ abort = abort || set_ref_in_nb_channel(copyID);
+#endif
+
+#ifdef FEAT_TIMERS
+ abort = abort || set_ref_in_timer(copyID);
+#endif
+
+#ifdef FEAT_QUICKFIX
+ abort = abort || set_ref_in_quickfix(copyID);
+#endif
+
+#ifdef FEAT_TERMINAL
+ abort = abort || set_ref_in_term(copyID);
+#endif
+
+#ifdef FEAT_PROP_POPUP
+ abort = abort || set_ref_in_popups(copyID);
+#endif
+
+ abort = abort || set_ref_in_classes(copyID);
+
+ if (!abort)
+ {
+ /*
+ * 2. Free lists and dictionaries that are not referenced.
+ */
+ did_free = free_unref_items(copyID);
+
+ /*
+ * 3. Check if any funccal can be freed now.
+ * This may call us back recursively.
+ */
+ free_unref_funccal(copyID, testing);
+ }
+ else if (p_verbose > 0)
+ {
+ verb_msg(_("Not enough memory to set references, garbage collection aborted!"));
+ }
+
+ return did_free;
+}
+
+/*
+ * Free lists, dictionaries, channels and jobs that are no longer referenced.
+ */
+ static int
+free_unref_items(int copyID)
+{
+ int did_free = FALSE;
+
+ // Let all "free" functions know that we are here. This means no
+ // dictionaries, lists, channels or jobs are to be freed, because we will
+ // do that here.
+ in_free_unref_items = TRUE;
+
+ /*
+ * PASS 1: free the contents of the items. We don't free the items
+ * themselves yet, so that it is possible to decrement refcount counters
+ */
+
+ // Go through the list of dicts and free items without this copyID.
+ did_free |= dict_free_nonref(copyID);
+
+ // Go through the list of lists and free items without this copyID.
+ did_free |= list_free_nonref(copyID);
+
+ // Go through the list of objects and free items without this copyID.
+ did_free |= object_free_nonref(copyID);
+
+ // Go through the list of classes and free items without this copyID.
+ did_free |= class_free_nonref(copyID);
+
+#ifdef FEAT_JOB_CHANNEL
+ // Go through the list of jobs and free items without the copyID. This
+ // must happen before doing channels, because jobs refer to channels, but
+ // the reference from the channel to the job isn't tracked.
+ did_free |= free_unused_jobs_contents(copyID, COPYID_MASK);
+
+ // Go through the list of channels and free items without the copyID.
+ did_free |= free_unused_channels_contents(copyID, COPYID_MASK);
+#endif
+
+ /*
+ * PASS 2: free the items themselves.
+ */
+ object_free_items(copyID);
+ dict_free_items(copyID);
+ list_free_items(copyID);
+
+#ifdef FEAT_JOB_CHANNEL
+ // Go through the list of jobs and free items without the copyID. This
+ // must happen before doing channels, because jobs refer to channels, but
+ // the reference from the channel to the job isn't tracked.
+ free_unused_jobs(copyID, COPYID_MASK);
+
+ // Go through the list of channels and free items without the copyID.
+ free_unused_channels(copyID, COPYID_MASK);
+#endif
+
+ in_free_unref_items = FALSE;
+
+ return did_free;
+}
+
+/*
+ * Mark all lists and dicts referenced through hashtab "ht" with "copyID".
+ * "list_stack" is used to add lists to be marked. Can be NULL.
+ *
+ * Returns TRUE if setting references failed somehow.
+ */
+ int
+set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack)
+{
+ int todo;
+ int abort = FALSE;
+ hashitem_T *hi;
+ hashtab_T *cur_ht;
+ ht_stack_T *ht_stack = NULL;
+ ht_stack_T *tempitem;
+
+ cur_ht = ht;
+ for (;;)
+ {
+ if (!abort)
+ {
+ // Mark each item in the hashtab. If the item contains a hashtab
+ // it is added to ht_stack, if it contains a list it is added to
+ // list_stack.
+ todo = (int)cur_ht->ht_used;
+ FOR_ALL_HASHTAB_ITEMS(cur_ht, hi, todo)
+ if (!HASHITEM_EMPTY(hi))
+ {
+ --todo;
+ abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID,
+ &ht_stack, list_stack);
+ }
+ }
+
+ if (ht_stack == NULL)
+ break;
+
+ // take an item from the stack
+ cur_ht = ht_stack->ht;
+ tempitem = ht_stack;
+ ht_stack = ht_stack->prev;
+ free(tempitem);
+ }
+
+ return abort;
+}
+
+#if defined(FEAT_LUA) || defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) \
+ || defined(PROTO)
+/*
+ * Mark a dict and its items with "copyID".
+ * Returns TRUE if setting references failed somehow.
+ */
+ int
+set_ref_in_dict(dict_T *d, int copyID)
+{
+ if (d != NULL && d->dv_copyID != copyID)
+ {
+ d->dv_copyID = copyID;
+ return set_ref_in_ht(&d->dv_hashtab, copyID, NULL);
+ }
+ return FALSE;
+}
+#endif
+
+/*
+ * Mark a list and its items with "copyID".
+ * Returns TRUE if setting references failed somehow.
+ */
+ int
+set_ref_in_list(list_T *ll, int copyID)
+{
+ if (ll != NULL && ll->lv_copyID != copyID)
+ {
+ ll->lv_copyID = copyID;
+ return set_ref_in_list_items(ll, copyID, NULL);
+ }
+ return FALSE;
+}
+
+/*
+ * Mark all lists and dicts referenced through list "l" with "copyID".
+ * "ht_stack" is used to add hashtabs to be marked. Can be NULL.
+ *
+ * Returns TRUE if setting references failed somehow.
+ */
+ int
+set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack)
+{
+ listitem_T *li;
+ int abort = FALSE;
+ list_T *cur_l;
+ list_stack_T *list_stack = NULL;
+ list_stack_T *tempitem;
+
+ cur_l = l;
+ for (;;)
+ {
+ if (!abort && cur_l->lv_first != &range_list_item)
+ // Mark each item in the list. If the item contains a hashtab
+ // it is added to ht_stack, if it contains a list it is added to
+ // list_stack.
+ for (li = cur_l->lv_first; !abort && li != NULL; li = li->li_next)
+ abort = abort || set_ref_in_item(&li->li_tv, copyID,
+ ht_stack, &list_stack);
+ if (list_stack == NULL)
+ break;
+
+ // take an item from the stack
+ cur_l = list_stack->list;
+ tempitem = list_stack;
+ list_stack = list_stack->prev;
+ free(tempitem);
+ }
+
+ return abort;
+}
+
+/*
+ * Mark the partial in callback 'cb' with "copyID".
+ */
+ int
+set_ref_in_callback(callback_T *cb, int copyID)
+{
+ typval_T tv;
+
+ if (cb->cb_name == NULL || *cb->cb_name == NUL || cb->cb_partial == NULL)
+ return FALSE;
+
+ tv.v_type = VAR_PARTIAL;
+ tv.vval.v_partial = cb->cb_partial;
+ return set_ref_in_item(&tv, copyID, NULL, NULL);
+}
+
+/*
+ * Mark the dict "dd" with "copyID".
+ * Also see set_ref_in_item().
+ */
+ static int
+set_ref_in_item_dict(
+ dict_T *dd,
+ int copyID,
+ ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ if (dd == NULL || dd->dv_copyID == copyID)
+ return FALSE;
+
+ // Didn't see this dict yet.
+ dd->dv_copyID = copyID;
+ if (ht_stack == NULL)
+ return set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack);
+
+ ht_stack_T *newitem = ALLOC_ONE(ht_stack_T);
+ if (newitem == NULL)
+ return TRUE;
+
+ newitem->ht = &dd->dv_hashtab;
+ newitem->prev = *ht_stack;
+ *ht_stack = newitem;
+
+ return FALSE;
+}
+
+/*
+ * Mark the list "ll" with "copyID".
+ * Also see set_ref_in_item().
+ */
+ static int
+set_ref_in_item_list(
+ list_T *ll,
+ int copyID,
+ ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ if (ll == NULL || ll->lv_copyID == copyID)
+ return FALSE;
+
+ // Didn't see this list yet.
+ ll->lv_copyID = copyID;
+ if (list_stack == NULL)
+ return set_ref_in_list_items(ll, copyID, ht_stack);
+
+ list_stack_T *newitem = ALLOC_ONE(list_stack_T);
+ if (newitem == NULL)
+ return TRUE;
+
+ newitem->list = ll;
+ newitem->prev = *list_stack;
+ *list_stack = newitem;
+
+ return FALSE;
+}
+
+/*
+ * Mark the partial "pt" with "copyID".
+ * Also see set_ref_in_item().
+ */
+ static int
+set_ref_in_item_partial(
+ partial_T *pt,
+ int copyID,
+ ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ if (pt == NULL || pt->pt_copyID == copyID)
+ return FALSE;
+
+ // Didn't see this partial yet.
+ pt->pt_copyID = copyID;
+
+ int abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
+
+ if (pt->pt_dict != NULL)
+ {
+ typval_T dtv;
+
+ dtv.v_type = VAR_DICT;
+ dtv.vval.v_dict = pt->pt_dict;
+ set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ }
+
+ if (pt->pt_obj != NULL)
+ {
+ typval_T objtv;
+
+ objtv.v_type = VAR_OBJECT;
+ objtv.vval.v_object = pt->pt_obj;
+ set_ref_in_item(&objtv, copyID, ht_stack, list_stack);
+ }
+
+ for (int i = 0; i < pt->pt_argc; ++i)
+ abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
+ ht_stack, list_stack);
+ // pt_funcstack is handled in set_ref_in_funcstacks()
+ // pt_loopvars is handled in set_ref_in_loopvars()
+
+ return abort;
+}
+
+#ifdef FEAT_JOB_CHANNEL
+/*
+ * Mark the job "pt" with "copyID".
+ * Also see set_ref_in_item().
+ */
+ static int
+set_ref_in_item_job(
+ job_T *job,
+ int copyID,
+ ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ typval_T dtv;
+
+ if (job == NULL || job->jv_copyID == copyID)
+ return FALSE;
+
+ job->jv_copyID = copyID;
+ if (job->jv_channel != NULL)
+ {
+ dtv.v_type = VAR_CHANNEL;
+ dtv.vval.v_channel = job->jv_channel;
+ set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ }
+ if (job->jv_exit_cb.cb_partial != NULL)
+ {
+ dtv.v_type = VAR_PARTIAL;
+ dtv.vval.v_partial = job->jv_exit_cb.cb_partial;
+ set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ }
+
+ return FALSE;
+}
+
+/*
+ * Mark the channel "ch" with "copyID".
+ * Also see set_ref_in_item().
+ */
+ static int
+set_ref_in_item_channel(
+ channel_T *ch,
+ int copyID,
+ ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ typval_T dtv;
+
+ if (ch == NULL || ch->ch_copyID == copyID)
+ return FALSE;
+
+ ch->ch_copyID = copyID;
+ for (ch_part_T part = PART_SOCK; part < PART_COUNT; ++part)
+ {
+ for (jsonq_T *jq = ch->ch_part[part].ch_json_head.jq_next;
+ jq != NULL; jq = jq->jq_next)
+ set_ref_in_item(jq->jq_value, copyID, ht_stack, list_stack);
+ for (cbq_T *cq = ch->ch_part[part].ch_cb_head.cq_next; cq != NULL;
+ cq = cq->cq_next)
+ if (cq->cq_callback.cb_partial != NULL)
+ {
+ dtv.v_type = VAR_PARTIAL;
+ dtv.vval.v_partial = cq->cq_callback.cb_partial;
+ set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ }
+ if (ch->ch_part[part].ch_callback.cb_partial != NULL)
+ {
+ dtv.v_type = VAR_PARTIAL;
+ dtv.vval.v_partial = ch->ch_part[part].ch_callback.cb_partial;
+ set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ }
+ }
+ if (ch->ch_callback.cb_partial != NULL)
+ {
+ dtv.v_type = VAR_PARTIAL;
+ dtv.vval.v_partial = ch->ch_callback.cb_partial;
+ set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ }
+ if (ch->ch_close_cb.cb_partial != NULL)
+ {
+ dtv.v_type = VAR_PARTIAL;
+ dtv.vval.v_partial = ch->ch_close_cb.cb_partial;
+ set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ }
+
+ return FALSE;
+}
+#endif
+
+/*
+ * Mark the class "cl" with "copyID".
+ * Also see set_ref_in_item().
+ */
+ int
+set_ref_in_item_class(
+ class_T *cl,
+ int copyID,
+ ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ int abort = FALSE;
+
+ if (cl == NULL || cl->class_copyID == copyID)
+ return FALSE;
+
+ cl->class_copyID = copyID;
+ if (cl->class_members_tv != NULL)
+ {
+ // The "class_members_tv" table is allocated only for regular classes
+ // and not for interfaces.
+ for (int i = 0; !abort && i < cl->class_class_member_count; ++i)
+ abort = abort || set_ref_in_item(
+ &cl->class_members_tv[i],
+ copyID, ht_stack, list_stack);
+ }
+
+ for (int i = 0; !abort && i < cl->class_class_function_count; ++i)
+ abort = abort || set_ref_in_func(NULL,
+ cl->class_class_functions[i], copyID);
+
+ for (int i = 0; !abort && i < cl->class_obj_method_count; ++i)
+ abort = abort || set_ref_in_func(NULL,
+ cl->class_obj_methods[i], copyID);
+
+ return abort;
+}
+
+/*
+ * Mark the object "cl" with "copyID".
+ * Also see set_ref_in_item().
+ */
+ static int
+set_ref_in_item_object(
+ object_T *obj,
+ int copyID,
+ ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ int abort = FALSE;
+
+ if (obj == NULL || obj->obj_copyID == copyID)
+ return FALSE;
+
+ obj->obj_copyID = copyID;
+
+ // The typval_T array is right after the object_T.
+ typval_T *mtv = (typval_T *)(obj + 1);
+ for (int i = 0; !abort
+ && i < obj->obj_class->class_obj_member_count; ++i)
+ abort = abort || set_ref_in_item(mtv + i, copyID,
+ ht_stack, list_stack);
+
+ return abort;
+}
+
+/*
+ * Mark all lists, dicts and other container types referenced through typval
+ * "tv" with "copyID".
+ * "list_stack" is used to add lists to be marked. Can be NULL.
+ * "ht_stack" is used to add hashtabs to be marked. Can be NULL.
+ *
+ * Returns TRUE if setting references failed somehow.
+ */
+ int
+set_ref_in_item(
+ typval_T *tv,
+ int copyID,
+ ht_stack_T **ht_stack,
+ list_stack_T **list_stack)
+{
+ int abort = FALSE;
+
+ switch (tv->v_type)
+ {
+ case VAR_DICT:
+ return set_ref_in_item_dict(tv->vval.v_dict, copyID,
+ ht_stack, list_stack);
+
+ case VAR_LIST:
+ return set_ref_in_item_list(tv->vval.v_list, copyID,
+ ht_stack, list_stack);
+
+ case VAR_FUNC:
+ {
+ abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
+ break;
+ }
+
+ case VAR_PARTIAL:
+ return set_ref_in_item_partial(tv->vval.v_partial, copyID,
+ ht_stack, list_stack);
+
+ case VAR_JOB:
+#ifdef FEAT_JOB_CHANNEL
+ return set_ref_in_item_job(tv->vval.v_job, copyID,
+ ht_stack, list_stack);
+#else
+ break;
+#endif
+
+ case VAR_CHANNEL:
+#ifdef FEAT_JOB_CHANNEL
+ return set_ref_in_item_channel(tv->vval.v_channel, copyID,
+ ht_stack, list_stack);
+#else
+ break;
+#endif
+
+ case VAR_CLASS:
+ return set_ref_in_item_class(tv->vval.v_class, copyID,
+ ht_stack, list_stack);
+
+ case VAR_OBJECT:
+ return set_ref_in_item_object(tv->vval.v_object, copyID,
+ ht_stack, list_stack);
+
+ case VAR_UNKNOWN:
+ case VAR_ANY:
+ case VAR_VOID:
+ case VAR_BOOL:
+ case VAR_SPECIAL:
+ case VAR_NUMBER:
+ case VAR_FLOAT:
+ case VAR_STRING:
+ case VAR_BLOB:
+ case VAR_TYPEALIAS:
+ case VAR_INSTR:
+ // Types that do not contain any other item
+ break;
+ }
+
+ return abort;
+}
+
+#endif
diff --git a/src/globals.h b/src/globals.h
index 2c00e5f..fb5c7b3 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1936,6 +1936,7 @@ EXTERN int reset_term_props_on_termresponse INIT(= FALSE);
EXTERN int disable_vterm_title_for_testing INIT(= FALSE);
EXTERN long override_sysinfo_uptime INIT(= -1);
EXTERN int override_autoload INIT(= FALSE);
+EXTERN int override_defcompile INIT(= FALSE);
EXTERN int ml_get_alloc_lines INIT(= FALSE);
EXTERN int ignore_unreachable_code_for_testing INIT(= FALSE);
diff --git a/src/gui.c b/src/gui.c
index 1953691..25662ef 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -5312,7 +5312,7 @@ gui_do_findrepl(
i = msg_scroll;
if (down)
{
- (void)do_search(NULL, '/', '/', ga.ga_data, 1L, searchflags, NULL);
+ (void)do_search(NULL, '/', '/', ga.ga_data, STRLEN(ga.ga_data), 1L, searchflags, NULL);
}
else
{
@@ -5320,7 +5320,7 @@ gui_do_findrepl(
// direction
p = vim_strsave_escaped(ga.ga_data, (char_u *)"?");
if (p != NULL)
- (void)do_search(NULL, '?', '?', p, 1L, searchflags, NULL);
+ (void)do_search(NULL, '?', '?', p, STRLEN(p), 1L, searchflags, NULL);
vim_free(p);
}
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
index 4d201fc..67ee531 100644
--- a/src/gui_gtk_x11.c
+++ b/src/gui_gtk_x11.c
@@ -2704,23 +2704,9 @@ global_event_filter(GdkXEvent *xev,
static void
mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
{
-// If you get an error message here, you still need to unpack the runtime
-// archive!
-#ifdef magick
-# undef magick
-#endif
- // A bit hackish, but avoids casting later and allows optimization
-# define static static const
-#define magick vim32x32
#include "../runtime/vim32x32.xpm"
-#undef magick
-#define magick vim16x16
#include "../runtime/vim16x16.xpm"
-#undef magick
-#define magick vim48x48
#include "../runtime/vim48x48.xpm"
-#undef magick
-# undef static
GdkWindow * const mainwin_win = gtk_widget_get_window(gui.mainwin);
@@ -2741,9 +2727,9 @@ mainwin_realize(GtkWidget *widget UNUSED, gpointer data UNUSED)
*/
GList *icons = NULL;
- icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim16x16));
- icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim32x32));
- icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data(vim48x48));
+ icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data((const char **)vim16x16));
+ icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data((const char **)vim32x32));
+ icons = g_list_prepend(icons, gdk_pixbuf_new_from_xpm_data((const char **)vim48x48));
gtk_window_set_icon_list(GTK_WINDOW(gui.mainwin), icons);
diff --git a/src/gui_x11.c b/src/gui_x11.c
index fc63658..edde6b5 100644
--- a/src/gui_x11.c
+++ b/src/gui_x11.c
@@ -1363,20 +1363,9 @@ gui_mch_init(void)
#else
// Use Pixmaps, looking much nicer.
-// If you get an error message here, you still need to unpack the runtime
-// archive!
-# ifdef magick
-# undef magick
-# endif
-# define magick vim32x32
# include "../runtime/vim32x32.xpm"
-# undef magick
-# define magick vim16x16
# include "../runtime/vim16x16.xpm"
-# undef magick
-# define magick vim48x48
# include "../runtime/vim48x48.xpm"
-# undef magick
static Pixmap icon = 0;
static Pixmap icon_mask = 0;
diff --git a/src/highlight.c b/src/highlight.c
index 9aa149f..a71a100 100644
--- a/src/highlight.c
+++ b/src/highlight.c
@@ -258,6 +258,8 @@ static char *(highlight_init_both[]) = {
"default link CurSearch Search",
"default link PmenuKind Pmenu",
"default link PmenuKindSel PmenuSel",
+ "default link PmenuMatch Pmenu",
+ "default link PmenuMatchSel PmenuSel",
"default link PmenuExtra Pmenu",
"default link PmenuExtraSel PmenuSel",
CENT("Normal cterm=NONE", "Normal gui=NONE"),
diff --git a/src/if_cscope.c b/src/if_cscope.c
index d9982ef..7b6fd92 100644
--- a/src/if_cscope.c
+++ b/src/if_cscope.c
@@ -541,7 +541,7 @@ cs_add_common(
char *ppath = NULL;
int i;
int len;
- int usedlen = 0;
+ size_t usedlen = 0;
char_u *fbuf = NULL;
// get the filename (arg1), expand it, and try to stat it
diff --git a/src/if_py_both.h b/src/if_py_both.h
index 3e5993b..e0fd3ea 100644
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -7325,12 +7325,11 @@ populate_module(PyObject *m)
return -1;
}
+# if PY_VERSION_HEX < 0x30c00a7
+ // find_module has been removed as of Python 3.12.0a7
if ((py_find_module = PyObject_GetAttrString(cls, "find_module")))
- {
- // find_module() is deprecated, this may stop working in some later
- // version.
ADD_OBJECT(m, "_find_module", py_find_module);
- }
+# endif
Py_DECREF(imp);
diff --git a/src/indent.c b/src/indent.c
index 1dfde7d..777db24 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -123,14 +123,22 @@ tabstop_padding(colnr_T col, int ts_arg, int *vts)
/*
* Find the size of the tab that covers a particular column.
+ *
+ * If this is being called as part of a shift operation, col is not the cursor
+ * column but is the column number to the left of the first non-whitespace
+ * character in the line. If the shift is to the left (left = TRUE), then
+ * return the size of the tab interval to the left of the column.
*/
int
-tabstop_at(colnr_T col, int ts, int *vts)
+tabstop_at(colnr_T col, int ts, int *vts, int left)
{
- int tabcount;
- colnr_T tabcol = 0;
- int t;
- int tab_size = 0;
+ int tabcount; // Number of tab stops in the list of variable
+ // tab stops.
+ colnr_T tabcol = 0; // Column of the tab stop under consideration.
+ int t; // Tabstop index in the list of variable tab
+ // stops.
+ int tab_size = 0; // Size of the tab stop interval to the right
+ // or left of the col.
if (vts == 0 || vts[0] == 0)
return ts;
@@ -141,11 +149,22 @@ tabstop_at(colnr_T col, int ts, int *vts)
tabcol += vts[t];
if (tabcol > col)
{
- tab_size = vts[t];
+ // If shifting left (left != 0), and if the column to the left of
+ // the first first non-blank character (col) in the line is
+ // already to the left of the first tabstop, set the shift amount
+ // (tab_size) to just enough to shift the line to the left margin.
+ // The value doesn't seem to matter as long as it is at least that
+ // distance.
+ if (left && (t == 1))
+ tab_size = col;
+ else
+ tab_size = vts[t - (left ? 1 : 0)];
break;
}
}
- if (t > tabcount)
+ if (t > tabcount) // If the value of the index t is beyond the
+ // end of the list, use the tab stop value at
+ // the end of the list.
tab_size = vts[tabcount];
return tab_size;
@@ -327,20 +346,20 @@ tabstop_first(int *ts)
long
get_sw_value(buf_T *buf)
{
- return get_sw_value_col(buf, 0);
+ return get_sw_value_col(buf, 0, FALSE);
}
/*
* Idem, using "pos".
*/
static long
-get_sw_value_pos(buf_T *buf, pos_T *pos)
+get_sw_value_pos(buf_T *buf, pos_T *pos, int left)
{
pos_T save_cursor = curwin->w_cursor;
long sw_value;
curwin->w_cursor = *pos;
- sw_value = get_sw_value_col(buf, get_nolist_virtcol());
+ sw_value = get_sw_value_col(buf, get_nolist_virtcol(), left);
curwin->w_cursor = save_cursor;
return sw_value;
}
@@ -349,23 +368,23 @@ get_sw_value_pos(buf_T *buf, pos_T *pos)
* Idem, using the first non-black in the current line.
*/
long
-get_sw_value_indent(buf_T *buf)
+get_sw_value_indent(buf_T *buf, int left)
{
pos_T pos = curwin->w_cursor;
pos.col = getwhitecols_curline();
- return get_sw_value_pos(buf, &pos);
+ return get_sw_value_pos(buf, &pos, left);
}
/*
* Idem, using virtual column "col".
*/
long
-get_sw_value_col(buf_T *buf, colnr_T col UNUSED)
+get_sw_value_col(buf_T *buf, colnr_T col UNUSED, int left UNUSED)
{
return buf->b_p_sw ? buf->b_p_sw :
#ifdef FEAT_VARTABS
- tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array);
+ tabstop_at(col, buf->b_p_ts, buf->b_p_vts_array, left);
#else
buf->b_p_ts;
#endif
diff --git a/src/insexpand.c b/src/insexpand.c
index 93a56a8..c673df9 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -113,6 +113,7 @@ struct compl_S
// cp_flags has CP_FREE_FNAME
int cp_flags; // CP_ values
int cp_number; // sequence number
+ int cp_score; // fuzzy match score
};
// values for cp_flags
@@ -147,13 +148,6 @@ static char_u *compl_leader = NULL;
static int compl_get_longest = FALSE; // put longest common string
// in compl_leader
-static int compl_no_insert = FALSE; // FALSE: select & insert
- // TRUE: noinsert
-static int compl_no_select = FALSE; // FALSE: select & insert
- // TRUE: noselect
-static int compl_longest = FALSE; // FALSE: insert full match
- // TRUE: insert longest prefix
-
// Selected one of the matches. When FALSE the match was edited or using the
// longest common string.
static int compl_used_match;
@@ -176,6 +170,7 @@ static int ctrl_x_mode = CTRL_X_NORMAL;
static int compl_matches = 0; // number of completion matches
static char_u *compl_pattern = NULL;
+static size_t compl_patternlen = 0;
static int compl_direction = FORWARD;
static int compl_shows_dir = FORWARD;
static int compl_pending = 0; // > 1 for postponed CTRL-N
@@ -206,6 +201,8 @@ static int compl_cont_status = 0;
static int compl_opt_refresh_always = FALSE;
static int compl_opt_suppress_empty = FALSE;
+static int compl_selected_item = -1;
+
static int ins_compl_add(char_u *str, int len, char_u *fname, char_u **cptext, typval_T *user_data, int cdir, int flags, int adup);
static void ins_compl_longest_match(compl_T *match);
static void ins_compl_del_pum(void);
@@ -1049,21 +1046,12 @@ ins_compl_long_shown_match(void)
}
/*
- * Set variables that store noselect and noinsert behavior from the
- * 'completeopt' value.
+ * Get the local or global value of 'completeopt' flags.
*/
- void
-completeopt_was_set(void)
+ unsigned int
+get_cot_flags(void)
{
- compl_no_insert = FALSE;
- compl_no_select = FALSE;
- compl_longest = FALSE;
- if (strstr((char *)p_cot, "noselect") != NULL)
- compl_no_select = TRUE;
- if (strstr((char *)p_cot, "noinsert") != NULL)
- compl_no_insert = TRUE;
- if (strstr((char *)p_cot, "longest") != NULL)
- compl_longest = TRUE;
+ return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags;
}
@@ -1110,7 +1098,7 @@ ins_compl_del_pum(void)
pum_wanted(void)
{
// 'completeopt' must contain "menu" or "menuone"
- if (vim_strchr(p_cot, 'm') == NULL)
+ if ((get_cot_flags() & COT_ANY_MENU) == 0)
return FALSE;
// The display looks bad on a B&W display.
@@ -1144,7 +1132,7 @@ pum_enough_matches(void)
compl = compl->cp_next;
} while (!is_first_match(compl));
- if (strstr((char *)p_cot, "menuone") != NULL)
+ if (get_cot_flags() & COT_MENUONE)
return (i >= 1);
return (i >= 2);
}
@@ -1212,6 +1200,19 @@ trigger_complete_changed_event(int cur)
#endif
/*
+ * pumitem qsort compare func
+ */
+ static int
+ins_compl_fuzzy_cmp(const void *a, const void *b)
+{
+ const int sa = (*(pumitem_T *)a).pum_score;
+ const int sb = (*(pumitem_T *)b).pum_score;
+ const int ia = (*(pumitem_T *)a).pum_idx;
+ const int ib = (*(pumitem_T *)b).pum_idx;
+ return sa == sb ? (ia == ib ? 0 : (ia < ib ? -1 : 1)) : (sa < sb ? 1 : -1);
+}
+
+/*
* Build a popup menu to show the completion matches.
* Returns the popup menu entry that should be selected. Returns -1 if nothing
* should be selected.
@@ -1226,6 +1227,10 @@ ins_compl_build_pum(void)
int i;
int cur = -1;
int lead_len = 0;
+ int max_fuzzy_score = 0;
+ unsigned int cur_cot_flags = get_cot_flags();
+ int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
+ int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
// Need to build the popup menu list.
compl_match_arraysize = 0;
@@ -1235,9 +1240,15 @@ ins_compl_build_pum(void)
do
{
+ // When 'completeopt' contains "fuzzy" and leader is not NULL or empty,
+ // set the cp_score for later comparisons.
+ if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0)
+ compl->cp_score = fuzzy_match_str(compl->cp_str, compl_leader);
+
if (!match_at_original_text(compl)
&& (compl_leader == NULL
- || ins_compl_equal(compl, compl_leader, lead_len)))
+ || ins_compl_equal(compl, compl_leader, lead_len)
+ || (compl_fuzzy_match && compl->cp_score > 0)))
++compl_match_arraysize;
compl = compl->cp_next;
} while (compl != NULL && !is_first_match(compl));
@@ -1254,15 +1265,22 @@ ins_compl_build_pum(void)
if (match_at_original_text(compl_shown_match))
shown_match_ok = TRUE;
+ if (compl_leader != NULL
+ && STRCMP(compl_leader, compl_orig_text) == 0
+ && shown_match_ok == FALSE)
+ compl_shown_match = compl_no_select ? compl_first_match
+ : compl_first_match->cp_next;
+
i = 0;
compl = compl_first_match;
do
{
if (!match_at_original_text(compl)
&& (compl_leader == NULL
- || ins_compl_equal(compl, compl_leader, lead_len)))
+ || ins_compl_equal(compl, compl_leader, lead_len)
+ || (compl_fuzzy_match && compl->cp_score > 0)))
{
- if (!shown_match_ok)
+ if (!shown_match_ok && !compl_fuzzy_match)
{
if (compl == compl_shown_match || did_find_shown_match)
{
@@ -1278,6 +1296,34 @@ ins_compl_build_pum(void)
shown_compl = compl;
cur = i;
}
+ else if (compl_fuzzy_match)
+ {
+ if (i == 0)
+ shown_compl = compl;
+ // Update the maximum fuzzy score and the shown match
+ // if the current item's score is higher
+ if (compl->cp_score > max_fuzzy_score)
+ {
+ did_find_shown_match = TRUE;
+ max_fuzzy_score = compl->cp_score;
+ compl_shown_match = compl;
+ shown_match_ok = TRUE;
+ }
+
+ // If there is no "no select" condition and the max fuzzy
+ // score is positive, or there is no completion leader or the
+ // leader length is zero, mark the shown match as valid and
+ // reset the current index.
+ if (!compl_no_select
+ && (max_fuzzy_score > 0
+ || (compl_leader == NULL || lead_len == 0)))
+ {
+ shown_match_ok = TRUE;
+ cur = 0;
+ if (match_at_original_text(compl_shown_match))
+ compl_shown_match = shown_compl;
+ }
+ }
if (compl->cp_text[CPT_ABBR] != NULL)
compl_match_array[i].pum_text =
@@ -1286,6 +1332,7 @@ ins_compl_build_pum(void)
compl_match_array[i].pum_text = compl->cp_str;
compl_match_array[i].pum_kind = compl->cp_text[CPT_KIND];
compl_match_array[i].pum_info = compl->cp_text[CPT_INFO];
+ compl_match_array[i].pum_score = compl->cp_score;
if (compl->cp_text[CPT_MENU] != NULL)
compl_match_array[i++].pum_extra =
compl->cp_text[CPT_MENU];
@@ -1293,7 +1340,7 @@ ins_compl_build_pum(void)
compl_match_array[i++].pum_extra = compl->cp_fname;
}
- if (compl == compl_shown_match)
+ if (compl == compl_shown_match && !compl_fuzzy_match)
{
did_find_shown_match = TRUE;
@@ -1313,6 +1360,15 @@ ins_compl_build_pum(void)
compl = compl->cp_next;
} while (compl != NULL && !is_first_match(compl));
+ if (compl_fuzzy_match && compl_leader != NULL && lead_len > 0)
+ {
+ for (i = 0; i < compl_match_arraysize; i++)
+ compl_match_array[i].pum_idx = i;
+ // sort by the largest score of fuzzy match
+ qsort(compl_match_array, (size_t)compl_match_arraysize,
+ sizeof(pumitem_T), ins_compl_fuzzy_cmp);
+ }
+
if (!shown_match_ok) // no displayed match at all
cur = -1;
@@ -1369,6 +1425,7 @@ ins_compl_show_pum(void)
// Use the cursor to get all wrapping and other settings right.
col = curwin->w_cursor.col;
curwin->w_cursor.col = compl_col;
+ compl_selected_item = cur;
pum_display(compl_match_array, compl_match_arraysize, cur);
curwin->w_cursor.col = col;
@@ -1386,6 +1443,15 @@ ins_compl_show_pum(void)
#define DICT_EXACT (2) // "dict" is the exact name of a file
/*
+ * Get current completion leader
+ */
+ char_u *
+ins_compl_leader(void)
+{
+ return compl_leader != NULL ? compl_leader : compl_orig_text;
+}
+
+/*
* Add any identifiers that match the given pattern "pat" in the list of
* dictionary files "dict_start" to the list of completions.
*/
@@ -1708,6 +1774,7 @@ ins_compl_free(void)
int i;
VIM_CLEAR(compl_pattern);
+ compl_patternlen = 0;
VIM_CLEAR(compl_leader);
if (compl_first_match == NULL)
@@ -1747,6 +1814,7 @@ ins_compl_clear(void)
compl_started = FALSE;
compl_matches = 0;
VIM_CLEAR(compl_pattern);
+ compl_patternlen = 0;
VIM_CLEAR(compl_leader);
edit_submode_extra = NULL;
VIM_CLEAR(compl_orig_text);
@@ -2404,9 +2472,8 @@ ins_compl_prep(int c)
if (ctrl_x_mode_not_defined_yet()
|| (ctrl_x_mode_normal() && !compl_started))
{
- compl_get_longest = compl_longest;
+ compl_get_longest = (get_cot_flags() & COT_LONGEST) != 0;
compl_used_match = TRUE;
-
}
if (ctrl_x_mode_not_defined_yet())
@@ -2878,6 +2945,10 @@ set_completion(colnr_T startcol, list_T *list)
int save_w_wrow = curwin->w_wrow;
int save_w_leftcol = curwin->w_leftcol;
int flags = CP_ORIGINAL_TEXT;
+ unsigned int cur_cot_flags = get_cot_flags();
+ int compl_longest = (cur_cot_flags & COT_LONGEST) != 0;
+ int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
+ int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
// If already doing completions stop it.
if (ctrl_x_mode_not_default())
@@ -3374,7 +3445,7 @@ done:
get_next_include_file_completion(int compl_type)
{
find_pattern_in_path(compl_pattern, compl_direction,
- (int)STRLEN(compl_pattern), FALSE, FALSE,
+ (int)compl_patternlen, FALSE, FALSE,
(compl_type == CTRL_X_PATH_DEFINES
&& !(compl_cont_status & CONT_SOL))
? FIND_DEFINE : FIND_ANY, 1L, ACTION_EXPAND,
@@ -3478,8 +3549,7 @@ get_next_cmdline_completion(void)
int num_matches;
if (expand_cmdline(&compl_xp, compl_pattern,
- (int)STRLEN(compl_pattern),
- &num_matches, &matches) == EXPAND_OK)
+ (int)compl_patternlen, &num_matches, &matches) == EXPAND_OK)
ins_compl_add_matches(num_matches, matches, FALSE);
}
@@ -3644,8 +3714,8 @@ get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
st->cur_match_pos, compl_direction, compl_pattern);
else
found_new_match = searchit(NULL, st->ins_buf, st->cur_match_pos,
- NULL, compl_direction, compl_pattern, 1L,
- SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
+ NULL, compl_direction, compl_pattern, compl_patternlen,
+ 1L, SEARCH_KEEP + SEARCH_NFMSG, RE_LAST, NULL);
--msg_silent;
if (!compl_started || st->set_match_pos)
{
@@ -4018,6 +4088,43 @@ ins_compl_show_filename(void)
}
/*
+ * Find a completion item when 'completeopt' contains "fuzzy".
+ */
+ static compl_T *
+find_comp_when_fuzzy(void)
+{
+ int score;
+ char_u* str;
+ int target_idx = -1;
+ int is_forward = compl_shows_dir_forward();
+ int is_backward = compl_shows_dir_backward();
+ compl_T *comp = NULL;
+
+ if (compl_match_array == NULL ||
+ (is_forward && compl_selected_item == compl_match_arraysize - 1)
+ || (is_backward && compl_selected_item == 0))
+ return compl_first_match;
+
+ if (is_forward)
+ target_idx = compl_selected_item + 1;
+ else if (is_backward)
+ target_idx = compl_selected_item == -1 ? compl_match_arraysize - 1
+ : compl_selected_item - 1;
+
+ score = compl_match_array[target_idx].pum_score;
+ str = compl_match_array[target_idx].pum_text;
+
+ comp = compl_first_match;
+ do {
+ if (comp->cp_score == score && (str == comp->cp_str || str == comp->cp_text[CPT_ABBR]))
+ return comp;
+ comp = comp->cp_next;
+ } while (comp != NULL && !is_first_match(comp));
+
+ return NULL;
+}
+
+/*
* Find the next set of matches for completion. Repeat the completion "todo"
* times. The number of matches found is returned in 'num_matches'.
*
@@ -4039,12 +4146,16 @@ find_next_completion_match(
{
int found_end = FALSE;
compl_T *found_compl = NULL;
+ unsigned int cur_cot_flags = get_cot_flags();
+ int compl_no_select = (cur_cot_flags & COT_NOSELECT) != 0;
+ int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
while (--todo >= 0)
{
if (compl_shows_dir_forward() && compl_shown_match->cp_next != NULL)
{
- compl_shown_match = compl_shown_match->cp_next;
+ compl_shown_match = compl_fuzzy_match && compl_match_array != NULL
+ ? find_comp_when_fuzzy() : compl_shown_match->cp_next;
found_end = (compl_first_match != NULL
&& (is_first_match(compl_shown_match->cp_next)
|| is_first_match(compl_shown_match)));
@@ -4053,7 +4164,8 @@ find_next_completion_match(
&& compl_shown_match->cp_prev != NULL)
{
found_end = is_first_match(compl_shown_match);
- compl_shown_match = compl_shown_match->cp_prev;
+ compl_shown_match = compl_fuzzy_match && compl_match_array != NULL
+ ? find_comp_when_fuzzy() : compl_shown_match->cp_prev;
found_end |= is_first_match(compl_shown_match);
}
else
@@ -4103,7 +4215,8 @@ find_next_completion_match(
if (!match_at_original_text(compl_shown_match)
&& compl_leader != NULL
&& !ins_compl_equal(compl_shown_match,
- compl_leader, (int)STRLEN(compl_leader)))
+ compl_leader, (int)STRLEN(compl_leader))
+ && !(compl_fuzzy_match && compl_shown_match->cp_score > 0))
++todo;
else
// Remember a matching item.
@@ -4153,13 +4266,18 @@ ins_compl_next(
int advance;
int started = compl_started;
buf_T *orig_curbuf = curbuf;
+ unsigned int cur_cot_flags = get_cot_flags();
+ int compl_no_insert = (cur_cot_flags & COT_NOINSERT) != 0;
+ int compl_fuzzy_match = (cur_cot_flags & COT_FUZZY) != 0;
// When user complete function return -1 for findstart which is next
// time of 'always', compl_shown_match become NULL.
if (compl_shown_match == NULL)
return -1;
- if (compl_leader != NULL && !match_at_original_text(compl_shown_match))
+ if (compl_leader != NULL
+ && !match_at_original_text(compl_shown_match)
+ && !compl_fuzzy_match)
// Update "compl_shown_match" to the actually shown match
ins_compl_update_shown_match();
@@ -4305,7 +4423,7 @@ ins_compl_check_keys(int frequency, int in_compl_func)
}
}
}
- if (compl_pending != 0 && !got_int && !compl_no_insert)
+ if (compl_pending != 0 && !got_int && !(cot_flags & COT_NOINSERT))
{
int todo = compl_pending > 0 ? compl_pending : -compl_pending;
@@ -4383,7 +4501,8 @@ ins_compl_use_match(int c)
/*
* Get the pattern, column and length for normal completion (CTRL-N CTRL-P
* completion)
- * Sets the global variables: compl_col, compl_length and compl_pattern.
+ * Sets the global variables: compl_col, compl_length, compl_pattern and
+ * compl_patternlen.
* Uses the global variables: compl_cont_status and ctrl_x_mode
*/
static int
@@ -4404,32 +4523,45 @@ get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
else
compl_pattern = vim_strnsave(line + compl_col, compl_length);
if (compl_pattern == NULL)
+ {
+ compl_patternlen = 0;
return FAIL;
+ }
}
else if (compl_status_adding())
{
char_u *prefix = (char_u *)"\\<";
+ size_t prefixlen = STRLEN_LITERAL("\\<");
// we need up to 2 extra chars for the prefix
compl_pattern = alloc(quote_meta(NULL, line + compl_col,
- compl_length) + 2);
+ compl_length) + prefixlen);
if (compl_pattern == NULL)
+ {
+ compl_patternlen = 0;
return FAIL;
+ }
if (!vim_iswordp(line + compl_col)
|| (compl_col > 0
&& (vim_iswordp(mb_prevptr(line, line + compl_col)))))
+ {
prefix = (char_u *)"";
+ prefixlen = 0;
+ }
STRCPY((char *)compl_pattern, prefix);
- (void)quote_meta(compl_pattern + STRLEN(prefix),
+ (void)quote_meta(compl_pattern + prefixlen,
line + compl_col, compl_length);
}
else if (--startcol < 0
|| !vim_iswordp(mb_prevptr(line, line + startcol + 1)))
{
// Match any word of at least two chars
- compl_pattern = vim_strsave((char_u *)"\\<\\k\\k");
+ compl_pattern = vim_strnsave((char_u *)"\\<\\k\\k", STRLEN_LITERAL("\\<\\k\\k"));
if (compl_pattern == NULL)
+ {
+ compl_patternlen = 0;
return FAIL;
+ }
compl_col += curs_col;
compl_length = 0;
}
@@ -4465,7 +4597,10 @@ get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
// alloc(7) is enough -- Acevedo
compl_pattern = alloc(7);
if (compl_pattern == NULL)
+ {
+ compl_patternlen = 0;
return FAIL;
+ }
STRCPY((char *)compl_pattern, "\\<");
(void)quote_meta(compl_pattern + 2, line + compl_col, 1);
STRCAT((char *)compl_pattern, "\\k");
@@ -4475,13 +4610,18 @@ get_normal_compl_info(char_u *line, int startcol, colnr_T curs_col)
compl_pattern = alloc(quote_meta(NULL, line + compl_col,
compl_length) + 2);
if (compl_pattern == NULL)
+ {
+ compl_patternlen = 0;
return FAIL;
+ }
STRCPY((char *)compl_pattern, "\\<");
(void)quote_meta(compl_pattern + 2, line + compl_col,
compl_length);
}
}
+ compl_patternlen = STRLEN(compl_pattern);
+
return OK;
}
@@ -4503,7 +4643,12 @@ get_wholeline_compl_info(char_u *line, colnr_T curs_col)
else
compl_pattern = vim_strnsave(line + compl_col, compl_length);
if (compl_pattern == NULL)
+ {
+ compl_patternlen = 0;
return FAIL;
+ }
+
+ compl_patternlen = STRLEN(compl_pattern);
return OK;
}
@@ -4533,7 +4678,12 @@ get_filename_compl_info(char_u *line, int startcol, colnr_T curs_col)
compl_length = (int)curs_col - startcol;
compl_pattern = addstar(line + compl_col, compl_length, EXPAND_FILES);
if (compl_pattern == NULL)
+ {
+ compl_patternlen = 0;
return FAIL;
+ }
+
+ compl_patternlen = STRLEN(compl_pattern);
return OK;
}
@@ -4547,9 +4697,13 @@ get_cmdline_compl_info(char_u *line, colnr_T curs_col)
{
compl_pattern = vim_strnsave(line, curs_col);
if (compl_pattern == NULL)
+ {
+ compl_patternlen = 0;
return FAIL;
+ }
+ compl_patternlen = curs_col;
set_cmd_context(&compl_xp, compl_pattern,
- (int)STRLEN(compl_pattern), curs_col, FALSE);
+ (int)compl_patternlen, curs_col, FALSE);
if (compl_xp.xp_context == EXPAND_UNSUCCESSFUL
|| compl_xp.xp_context == EXPAND_NOTHING)
// No completion possible, use an empty pattern to get a
@@ -4647,8 +4801,12 @@ get_userdefined_compl_info(colnr_T curs_col UNUSED)
compl_length = curs_col - compl_col;
compl_pattern = vim_strnsave(line + compl_col, compl_length);
if (compl_pattern == NULL)
+ {
+ compl_patternlen = 0;
return FAIL;
+ }
+ compl_patternlen = compl_length;
ret = OK;
#endif
@@ -4685,8 +4843,12 @@ get_spell_compl_info(int startcol UNUSED, colnr_T curs_col UNUSED)
line = ml_get(curwin->w_cursor.lnum);
compl_pattern = vim_strnsave(line + compl_col, compl_length);
if (compl_pattern == NULL)
+ {
+ compl_patternlen = 0;
return FAIL;
+ }
+ compl_patternlen = compl_length;
ret = OK;
#endif
@@ -4907,6 +5069,7 @@ ins_compl_start(void)
-1, NULL, NULL, NULL, 0, flags, FALSE) != OK)
{
VIM_CLEAR(compl_pattern);
+ compl_patternlen = 0;
VIM_CLEAR(compl_orig_text);
return FAIL;
}
@@ -5195,7 +5358,7 @@ spell_back_to_badword(void)
{
pos_T tpos = curwin->w_cursor;
- spell_bad_len = spell_move_to(curwin, BACKWARD, TRUE, TRUE, NULL);
+ spell_bad_len = spell_move_to(curwin, BACKWARD, SMT_ALL, TRUE, NULL);
if (curwin->w_cursor.col != tpos.col)
start_arrow(&tpos);
}
diff --git a/src/link.sh b/src/link.sh
index e4030de..6e5c50f 100755
--- a/src/link.sh
+++ b/src/link.sh
@@ -53,7 +53,7 @@ else
if sh link_$PROG.cmd; then
touch auto/link.sed
cp link_$PROG.cmd linkit_$PROG.sh
- for libname in SM ICE nsl dnet dnet_stub inet socket dir elf iconv Xt Xmu Xp Xpm X11 Xdmcp x w perl dl pthread thread readline m crypt attr; do
+ for libname in SM ICE nsl dnet dnet_stub inet socket dir iconv Xt Xmu Xp Xpm X11 Xdmcp x w perl dl pthread thread readline m crypt attr; do
cont=yes
while test -n "$cont"; do
if grep "l$libname " linkit_$PROG.sh >/dev/null; then
diff --git a/src/list.c b/src/list.c
index e9f1ae3..9479b4b 100644
--- a/src/list.c
+++ b/src/list.c
@@ -3194,4 +3194,28 @@ f_reduce(typval_T *argvars, typval_T *rettv)
blob_reduce(argvars, &argvars[1], rettv);
}
+/*
+ * slice() function
+ */
+ void
+f_slice(typval_T *argvars, typval_T *rettv)
+{
+ if (in_vim9script()
+ && ((argvars[0].v_type != VAR_STRING
+ && argvars[0].v_type != VAR_LIST
+ && argvars[0].v_type != VAR_BLOB
+ && check_for_list_arg(argvars, 0) == FAIL)
+ || check_for_number_arg(argvars, 1) == FAIL
+ || check_for_opt_number_arg(argvars, 2) == FAIL))
+ return;
+
+ if (check_can_index(&argvars[0], TRUE, FALSE) != OK)
+ return;
+
+ copy_tv(argvars, rettv);
+ eval_index_inner(rettv, TRUE, argvars + 1,
+ argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2,
+ TRUE, NULL, 0, FALSE);
+}
+
#endif // defined(FEAT_EVAL)
diff --git a/src/misc1.c b/src/misc1.c
index c5a0c38..8348488 100644
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -497,12 +497,17 @@ plines_win_col(win_T *wp, linenr_T lnum, long column)
return lines;
}
+/*
+ * Return number of window lines the physical line range from "first" until
+ * "last" will occupy in window "wp". Takes into account folding, 'wrap',
+ * topfill and filler lines beyond the end of the buffer. Limit to "max" lines.
+ */
int
-plines_m_win(win_T *wp, linenr_T first, linenr_T last, int limit_winheight)
+plines_m_win(win_T *wp, linenr_T first, linenr_T last, int max)
{
int count = 0;
- while (first <= last && (!limit_winheight || count < wp->w_height))
+ while (first <= last && count < max)
{
#ifdef FEAT_FOLDING
int x;
@@ -531,9 +536,7 @@ plines_m_win(win_T *wp, linenr_T first, linenr_T last, int limit_winheight)
if (first == wp->w_buffer->b_ml.ml_line_count + 1)
count += diff_check_fill(wp, first);
#endif
- if (limit_winheight && count > wp->w_height)
- return wp->w_height;
- return (count);
+ return MIN(max, count);
}
int
diff --git a/src/mouse.c b/src/mouse.c
index 247c6df..4e10e72 100644
--- a/src/mouse.c
+++ b/src/mouse.c
@@ -3029,16 +3029,22 @@ mouse_comp_pos(
if (win->w_skipcol > 0 && lnum == win->w_topline)
{
- // Adjust for 'smoothscroll' clipping the top screen lines.
- // A similar formula is used in curs_columns().
int width1 = win->w_width - win_col_off(win);
- int skip_lines = 0;
- if (win->w_skipcol > width1)
- skip_lines = (win->w_skipcol - width1)
+
+ if (width1 > 0)
+ {
+ int skip_lines = 0;
+
+ // Adjust for 'smoothscroll' clipping the top screen lines.
+ // A similar formula is used in curs_columns().
+ if (win->w_skipcol > width1)
+ skip_lines = (win->w_skipcol - width1)
/ (width1 + win_col_off2(win)) + 1;
- else if (win->w_skipcol > 0)
- skip_lines = 1;
- count -= skip_lines;
+ else if (win->w_skipcol > 0)
+ skip_lines = 1;
+
+ count -= skip_lines;
+ }
}
if (count > row)
diff --git a/src/move.c b/src/move.c
index 1b6e003..71654dd 100644
--- a/src/move.c
+++ b/src/move.c
@@ -319,6 +319,7 @@ update_topline(void)
redraw_later(UPD_NOT_VALID);
curwin->w_topline = 1;
curwin->w_botline = 2;
+ curwin->w_skipcol = 0;
curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP;
curwin->w_scbind_pos = 1;
}
@@ -1455,7 +1456,7 @@ textpos2screenpos(
is_folded = hasFoldingWin(wp, lnum, &lnum, NULL, TRUE, NULL);
#endif
- row = plines_m_win(wp, wp->w_topline, lnum - 1, FALSE);
+ row = plines_m_win(wp, wp->w_topline, lnum - 1, INT_MAX);
// "row" should be the screen line where line "lnum" begins, which can
// be negative if "lnum" is "w_topline" and "w_skipcol" is non-zero.
row -= adjust_plines_for_skipcol(wp);
@@ -1629,6 +1630,7 @@ static void cursor_correct_sms(void)
int width2 = width1 + curwin_col_off2();
int so_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
int space_cols = (curwin->w_height - 1) * width2;
+ int overlap, top, bot;
int size = so == 0 ? 0 : win_linetabsize(curwin, curwin->w_topline,
ml_get(curwin->w_topline), (colnr_T)MAXCOL);
@@ -1638,16 +1640,16 @@ static void cursor_correct_sms(void)
so_cols = space_cols / 2; // Not enough room: put cursor in the middle.
// Not enough screen lines in topline: ignore 'scrolloff'.
- while (so_cols > size && so_cols - width2 >= width1)
+ while (so_cols > size && so_cols - width2 >= width1 && width1 > 0)
so_cols -= width2;
if (so_cols >= width1 && so_cols > size)
so_cols -= width1;
// If there is no marker or we have non-zero scrolloff, just ignore it.
- int overlap = (curwin->w_skipcol == 0 || so_cols != 0) ? 0
+ overlap = (curwin->w_skipcol == 0 || so_cols != 0) ? 0
: sms_marker_overlap(curwin, -1);
- int top = curwin->w_skipcol + overlap + so_cols;
- int bot = curwin->w_skipcol + width1 + (curwin->w_height - 1) * width2
+ top = curwin->w_skipcol + overlap + so_cols;
+ bot = curwin->w_skipcol + width1 + (curwin->w_height - 1) * width2
- so_cols;
validate_virtcol();
colnr_T col = curwin->w_virtcol;
@@ -2025,6 +2027,8 @@ adjust_skipcol(void)
long so = get_scrolloff_value();
int scrolloff_cols = so == 0 ? 0 : width1 + (so - 1) * width2;
int scrolled = FALSE;
+ int row = 0;
+ int overlap, col;
validate_cheight();
if (curwin->w_cline_height == curwin->w_height
@@ -2039,7 +2043,7 @@ adjust_skipcol(void)
}
validate_virtcol();
- int overlap = sms_marker_overlap(curwin, -1);
+ overlap = sms_marker_overlap(curwin, -1);
while (curwin->w_skipcol > 0
&& curwin->w_virtcol < curwin->w_skipcol + overlap + scrolloff_cols)
{
@@ -2057,8 +2061,19 @@ adjust_skipcol(void)
return; // don't scroll in the other direction now
}
- int col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols;
- int row = 0;
+ col = curwin->w_virtcol + scrolloff_cols;
+
+ // Avoid adjusting for 'scrolloff' beyond the text line height.
+ if (scrolloff_cols > 0)
+ {
+ int size = win_linetabsize(curwin, curwin->w_topline,
+ ml_get(curwin->w_topline), (colnr_T)MAXCOL);
+ size = width1 + width2 * ((size - width1 + width2 - 1) / width2);
+ while (col > size)
+ col -= width2;
+ }
+ col -= curwin->w_skipcol;
+
if (col >= width1)
{
col -= width1;
@@ -2616,12 +2631,14 @@ scroll_cursor_bot(int min_scroll, int set_topbot)
plines_win
#endif
(curwin, curwin->w_topline, FALSE);
- int skip_lines = 0;
int width1 = curwin->w_width - curwin_col_off();
+
if (width1 > 0)
{
int width2 = width1 + curwin_col_off2();
- // similar formula is used in curs_columns()
+ int skip_lines = 0;
+
+ // A similar formula is used in curs_columns().
if (curwin->w_skipcol > width1)
skip_lines += (curwin->w_skipcol - width1) / width2 + 1;
else if (curwin->w_skipcol > 0)
@@ -2781,7 +2798,9 @@ scroll_cursor_bot(int min_scroll, int set_topbot)
}
curwin->w_valid |= VALID_TOPLINE;
- cursor_correct_sms();
+ // Make sure cursor is still visible after adjusting skipcol for "zb".
+ if (set_topbot)
+ cursor_correct_sms();
}
/*
@@ -3155,9 +3174,10 @@ static int get_scroll_overlap(int dir)
/*
* Scroll "count" lines with 'smoothscroll' in direction "dir". Return TRUE
- * when scrolling happened.
+ * when scrolling happened. Adjust "curscount" for scrolling different amount of
+ * lines when 'smoothscroll' is disabled.
*/
-static int scroll_with_sms(int dir, long count)
+static int scroll_with_sms(int dir, long count, long *curscount)
{
int prev_sms = curwin->w_p_sms;
colnr_T prev_skipcol = curwin->w_skipcol;
@@ -3180,7 +3200,10 @@ static int scroll_with_sms(int dir, long count)
fixdir = dir * -1;
while (curwin->w_skipcol > 0
&& curwin->w_topline < curbuf->b_ml.ml_line_count)
+ {
scroll_redraw(fixdir == FORWARD, 1);
+ *curscount += (fixdir == dir ? 1 : -1);
+ }
}
curwin->w_p_sms = prev_sms;
@@ -3217,21 +3240,27 @@ pagescroll(int dir, long count, int half)
curwin->w_p_scr = MIN(curwin->w_height, count);
count = MIN(curwin->w_height, curwin->w_p_scr);
- int curscount = count;
+ long curscount = count;
// Adjust count so as to not reveal end of buffer lines.
- if (dir == FORWARD)
+ if (dir == FORWARD
+ && (curwin->w_topline + curwin->w_height + count > buflen
+#ifdef FEAT_FOLDING
+ || hasAnyFolding(curwin)
+#endif
+ ))
{
int n = plines_correct_topline(curwin, curwin->w_topline, FALSE);
if (n - count < curwin->w_height && curwin->w_topline < buflen)
- n += plines_m_win(curwin, curwin->w_topline + 1, buflen, FALSE);
- if (n - count < curwin->w_height)
+ n += plines_m_win(curwin, curwin->w_topline + 1, buflen,
+ curwin->w_height + count);
+ if (n < curwin->w_height + count)
count = n - curwin->w_height;
}
// (Try to) scroll the window unless already at the end of the buffer.
if (count > 0)
{
- nochange = scroll_with_sms(dir, count);
+ nochange = scroll_with_sms(dir, count, &curscount);
curwin->w_cursor.lnum = prev_lnum;
curwin->w_cursor.col = prev_col;
curwin->w_curswant = prev_curswant;
@@ -3250,7 +3279,7 @@ pagescroll(int dir, long count, int half)
// Scroll [count] times 'window' or current window height lines.
count *= ((ONE_WINDOW && p_window > 0 && p_window < Rows - 1) ?
MAX(1, p_window - 2) : get_scroll_overlap(dir));
- nochange = scroll_with_sms(dir, count);
+ nochange = scroll_with_sms(dir, count, &count);
// Place cursor at top or bottom of window.
validate_botline();
diff --git a/src/normal.c b/src/normal.c
index fef2826..b55d941 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -61,7 +61,7 @@ static void nv_end(cmdarg_T *cap);
static void nv_dollar(cmdarg_T *cap);
static void nv_search(cmdarg_T *cap);
static void nv_next(cmdarg_T *cap);
-static int normal_search(cmdarg_T *cap, int dir, char_u *pat, int opt, int *wrapped);
+static int normal_search(cmdarg_T *cap, int dir, char_u *pat, size_t patlen, int opt, int *wrapped);
static void nv_csearch(cmdarg_T *cap);
static void nv_brackets(cmdarg_T *cap);
static void nv_percent(cmdarg_T *cap);
@@ -2169,6 +2169,7 @@ find_decl(
int flags_arg) // flags passed to searchit()
{
char_u *pat;
+ size_t patlen;
pos_T old_pos;
pos_T par_pos;
pos_T found_pos;
@@ -2185,8 +2186,9 @@ find_decl(
// Put "\V" before the pattern to avoid that the special meaning of "."
// and "~" causes trouble.
- sprintf((char *)pat, vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s",
- len, ptr);
+ patlen = vim_snprintf((char *)pat, len + 7,
+ vim_iswordp(ptr) ? "\\V\\<%.*s\\>" : "\\V%.*s", len, ptr);
+
old_pos = curwin->w_cursor;
save_p_ws = p_ws;
save_p_scs = p_scs;
@@ -2215,7 +2217,7 @@ find_decl(
for (;;)
{
t = searchit(curwin, curbuf, &curwin->w_cursor, NULL, FORWARD,
- pat, 1L, searchflags, RE_LAST, NULL);
+ pat, patlen, 1L, searchflags, RE_LAST, NULL);
if (curwin->w_cursor.lnum >= old_pos.lnum)
t = FAIL; // match after start is failure too
@@ -2593,7 +2595,7 @@ nv_zg_zw(cmdarg_T *cap, int nchar)
// off this fails and find_ident_under_cursor() is
// used below.
emsg_off++;
- len = spell_move_to(curwin, FORWARD, TRUE, TRUE, NULL);
+ len = spell_move_to(curwin, FORWARD, SMT_ALL, TRUE, NULL);
emsg_off--;
if (len != 0 && curwin->w_cursor.col <= pos.col)
ptr = ml_get_pos(&curwin->w_cursor);
@@ -3332,7 +3334,8 @@ nv_K_getcmd(
char_u **ptr_arg,
int n,
char_u *buf,
- unsigned buflen)
+ size_t bufsize,
+ size_t *buflen)
{
char_u *ptr = *ptr_arg;
int isman;
@@ -3342,6 +3345,7 @@ nv_K_getcmd(
{
// in the help buffer
STRCPY(buf, "he! ");
+ *buflen = STRLEN_LITERAL("he! ");
return n;
}
@@ -3349,10 +3353,9 @@ nv_K_getcmd(
{
// 'keywordprog' is an ex command
if (cap->count0 != 0)
- vim_snprintf((char *)buf, buflen, "%s %ld", kp, cap->count0);
+ *buflen = vim_snprintf((char *)buf, bufsize, "%s %ld ", kp, cap->count0);
else
- STRCPY(buf, kp);
- STRCAT(buf, " ");
+ *buflen = vim_snprintf((char *)buf, bufsize, "%s ", kp);
return n;
}
@@ -3377,19 +3380,16 @@ nv_K_getcmd(
isman = (STRCMP(kp, "man") == 0);
isman_s = (STRCMP(kp, "man -s") == 0);
if (cap->count0 != 0 && !(isman || isman_s))
- sprintf((char *)buf, ".,.+%ld", cap->count0 - 1);
+ *buflen = vim_snprintf((char *)buf, bufsize, ".,.+%ld! ", cap->count0 - 1);
+ else
+ *buflen = vim_snprintf((char *)buf, bufsize, "! ");
- STRCAT(buf, "! ");
if (cap->count0 == 0 && isman_s)
- STRCAT(buf, "man");
+ *buflen += vim_snprintf((char *)buf + *buflen, bufsize - *buflen, "man ");
else
- STRCAT(buf, kp);
- STRCAT(buf, " ");
+ *buflen += vim_snprintf((char *)buf + *buflen, bufsize - *buflen, "%s ", kp);
if (cap->count0 != 0 && (isman || isman_s))
- {
- sprintf((char *)buf + STRLEN(buf), "%ld", cap->count0);
- STRCAT(buf, " ");
- }
+ *buflen += vim_snprintf((char *)buf + *buflen, bufsize - *buflen, "%ld ", cap->count0);
*ptr_arg = ptr;
return n;
@@ -3408,7 +3408,8 @@ nv_ident(cmdarg_T *cap)
{
char_u *ptr = NULL;
char_u *buf;
- unsigned buflen;
+ size_t bufsize;
+ size_t buflen;
char_u *newbuf;
char_u *p;
char_u *kp; // value of 'keywordprg'
@@ -3463,11 +3464,12 @@ nv_ident(cmdarg_T *cap)
return;
}
kp_ex = (*kp == ':');
- buflen = (unsigned)(n * 2 + 30 + STRLEN(kp));
- buf = alloc(buflen);
+ bufsize = (size_t)(n * 2 + 30 + STRLEN(kp));
+ buf = alloc(bufsize);
if (buf == NULL)
return;
buf[0] = NUL;
+ buflen = 0;
switch (cmdchar)
{
@@ -3481,12 +3483,15 @@ nv_ident(cmdarg_T *cap)
curwin->w_cursor.col = (colnr_T) (ptr - ml_get_curline());
if (!g_cmd && vim_iswordp(ptr))
+ {
STRCPY(buf, "\\<");
+ buflen = STRLEN_LITERAL("\\<");
+ }
no_smartcase = TRUE; // don't use 'smartcase' now
break;
case 'K':
- n = nv_K_getcmd(cap, kp, kp_help, kp_ex, &ptr, n, buf, buflen);
+ n = nv_K_getcmd(cap, kp, kp_help, kp_ex, &ptr, n, buf, bufsize, &buflen);
if (n == 0)
return;
break;
@@ -3495,30 +3500,47 @@ nv_ident(cmdarg_T *cap)
tag_cmd = TRUE;
#ifdef FEAT_CSCOPE
if (p_cst)
+ {
STRCPY(buf, "cstag ");
+ buflen = STRLEN_LITERAL("cstag ");
+ }
else
#endif
+ {
STRCPY(buf, "ts ");
+ buflen = STRLEN_LITERAL("ts ");
+ }
break;
default:
tag_cmd = TRUE;
if (curbuf->b_help)
+ {
STRCPY(buf, "he! ");
+ buflen = STRLEN_LITERAL("he! ");
+ }
else
{
if (g_cmd)
+ {
STRCPY(buf, "tj ");
+ buflen = STRLEN_LITERAL("tj ");
+ }
else if (cap->count0 == 0)
+ {
STRCPY(buf, "ta ");
+ buflen = STRLEN_LITERAL("ta ");
+ }
else
- sprintf((char *)buf, ":%ldta ", cap->count0);
+ buflen = vim_snprintf((char *)buf, bufsize, ":%ldta ", cap->count0);
}
}
// Now grab the chars in the identifier
if (cmdchar == 'K' && !kp_help)
{
+ size_t plen;
+
ptr = vim_strnsave(ptr, n);
if (kp_ex)
// Escape the argument properly for an Ex command
@@ -3532,7 +3554,8 @@ nv_ident(cmdarg_T *cap)
vim_free(buf);
return;
}
- newbuf = vim_realloc(buf, STRLEN(buf) + STRLEN(p) + 1);
+ plen = STRLEN(p);
+ newbuf = vim_realloc(buf, buflen + plen + 1);
if (newbuf == NULL)
{
vim_free(buf);
@@ -3540,7 +3563,8 @@ nv_ident(cmdarg_T *cap)
return;
}
buf = newbuf;
- STRCAT(buf, p);
+ STRCPY(buf + buflen, p);
+ buflen += plen;
vim_free(p);
}
else
@@ -3560,12 +3584,13 @@ nv_ident(cmdarg_T *cap)
else
aux_ptr = (char_u *)"\\|\"\n*?[";
- p = buf + STRLEN(buf);
+ p = buf + buflen;
while (n-- > 0)
{
// put a backslash before \ and some others
if (vim_strchr(aux_ptr, *ptr) != NULL)
*p++ = '\\';
+
// When current byte is a part of multibyte character, copy all
// bytes of that character.
if (has_mbyte)
@@ -3579,6 +3604,7 @@ nv_ident(cmdarg_T *cap)
*p++ = *ptr++;
}
*p = NUL;
+ buflen = p - buf;
}
// Execute the command.
@@ -3587,13 +3613,16 @@ nv_ident(cmdarg_T *cap)
if (!g_cmd && (has_mbyte
? vim_iswordp(mb_prevptr(ml_get_curline(), ptr))
: vim_iswordc(ptr[-1])))
- STRCAT(buf, "\\>");
+ {
+ STRCPY(buf + buflen, "\\>");
+ buflen += STRLEN_LITERAL("\\>");
+ }
// put pattern in search history
init_history();
- add_to_history(HIST_SEARCH, buf, TRUE, NUL);
+ add_to_history(HIST_SEARCH, buf, buflen, TRUE, NUL);
- (void)normal_search(cap, cmdchar == '*' ? '/' : '?', buf, 0, NULL);
+ (void)normal_search(cap, cmdchar == '*' ? '/' : '?', buf, buflen, 0, NULL);
}
else
{
@@ -4117,7 +4146,7 @@ nv_search(cmdarg_T *cap)
return;
}
- (void)normal_search(cap, cap->cmdchar, cap->searchbuf,
+ (void)normal_search(cap, cap->cmdchar, cap->searchbuf, STRLEN(cap->searchbuf),
(cap->arg || !EQUAL_POS(save_cursor, curwin->w_cursor))
? 0 : SEARCH_MARK, NULL);
}
@@ -4132,7 +4161,7 @@ nv_next(cmdarg_T *cap)
{
pos_T old = curwin->w_cursor;
int wrapped = FALSE;
- int i = normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, &wrapped);
+ int i = normal_search(cap, 0, NULL, 0, SEARCH_MARK | cap->arg, &wrapped);
if (i == 1 && !wrapped && EQUAL_POS(old, curwin->w_cursor))
{
@@ -4140,7 +4169,7 @@ nv_next(cmdarg_T *cap)
// happen when an offset is given and the cursor is on the last char
// in the buffer: Repeat with count + 1.
cap->count1 += 1;
- (void)normal_search(cap, 0, NULL, SEARCH_MARK | cap->arg, NULL);
+ (void)normal_search(cap, 0, NULL, 0, SEARCH_MARK | cap->arg, NULL);
cap->count1 -= 1;
}
@@ -4161,6 +4190,7 @@ normal_search(
cmdarg_T *cap,
int dir,
char_u *pat,
+ size_t patlen,
int opt, // extra flags for do_search()
int *wrapped)
{
@@ -4176,7 +4206,7 @@ normal_search(
curwin->w_set_curswant = TRUE;
CLEAR_FIELD(sia);
- i = do_search(cap->oap, dir, dir, pat, cap->count1,
+ i = do_search(cap->oap, dir, dir, pat, patlen, cap->count1,
opt | SEARCH_OPT | SEARCH_ECHO | SEARCH_MSG, &sia);
if (wrapped != NULL)
*wrapped = sia.sa_wrapped;
@@ -4201,6 +4231,7 @@ normal_search(
// "/$" will put the cursor after the end of the line, may need to
// correct that here
check_cursor();
+
return i;
}
@@ -4529,13 +4560,15 @@ nv_brackets(cmdarg_T *cap)
#endif
#ifdef FEAT_SPELL
- // "[s", "[S", "]s" and "]S": move to next spell error.
- else if (cap->nchar == 's' || cap->nchar == 'S')
+ // "[r", "[s", "[S", "]r", "]s" and "]S": move to next spell error.
+ else if (cap->nchar == 'r' || cap->nchar == 's' || cap->nchar == 'S')
{
setpcmark();
for (n = 0; n < cap->count1; ++n)
if (spell_move_to(curwin, cap->cmdchar == ']' ? FORWARD : BACKWARD,
- cap->nchar == 's' ? TRUE : FALSE, FALSE, NULL) == 0)
+ cap->nchar == 's' ? SMT_ALL :
+ cap->nchar == 'r' ? SMT_RARE :
+ SMT_BAD, FALSE, NULL) == 0)
{
clearopbeep(cap->oap);
break;
@@ -6663,29 +6696,40 @@ adjust_for_sel(cmdarg_T *cap)
int
unadjust_for_sel(void)
{
- pos_T *pp;
-
if (*p_sel == 'e' && !EQUAL_POS(VIsual, curwin->w_cursor))
+ return unadjust_for_sel_inner(LT_POS(VIsual, curwin->w_cursor)
+ ? &curwin->w_cursor : &VIsual);
+ return FALSE;
+}
+
+/*
+ * Move position "*pp" back one character for 'selection' == "exclusive".
+ * Returns TRUE when backed up to the previous line.
+ */
+ int
+unadjust_for_sel_inner(pos_T *pp)
+{
+ colnr_T cs, ce;
+
+ if (pp->coladd > 0)
+ --pp->coladd;
+ else if (pp->col > 0)
{
- if (LT_POS(VIsual, curwin->w_cursor))
- pp = &curwin->w_cursor;
- else
- pp = &VIsual;
- if (pp->coladd > 0)
- --pp->coladd;
- else
- if (pp->col > 0)
- {
- --pp->col;
- mb_adjustpos(curbuf, pp);
- }
- else if (pp->lnum > 1)
+ --pp->col;
+ mb_adjustpos(curbuf, pp);
+ if (virtual_active())
{
- --pp->lnum;
- pp->col = ml_get_len(pp->lnum);
- return TRUE;
+ getvcol(curwin, pp, &cs, NULL, &ce);
+ pp->coladd = ce - cs;
}
}
+ else if (pp->lnum > 1)
+ {
+ --pp->lnum;
+ pp->col = ml_get_len(pp->lnum);
+ return TRUE;
+ }
+
return FALSE;
}
diff --git a/src/ops.c b/src/ops.c
index e3e7827..eb75c34 100644
--- a/src/ops.c
+++ b/src/ops.c
@@ -231,7 +231,10 @@ shift_line(
{
vimlong_T count;
int i, j;
- int sw_val = trim_to_int(get_sw_value_indent(curbuf));
+ int sw_val = trim_to_int(get_sw_value_indent(curbuf, left));
+
+ if (sw_val == 0)
+ sw_val = 1; // shouldn't happen, just in case
count = get_indent(); // get current indent
@@ -283,7 +286,7 @@ shift_block(oparg_T *oap, int amount)
char_u *newp, *oldp;
size_t newlen, oldlen;
int oldcol = curwin->w_cursor.col;
- int sw_val = (int)get_sw_value_indent(curbuf);
+ int sw_val = (int)get_sw_value_indent(curbuf, left);
int ts_val = (int)curbuf->b_p_ts;
struct block_def bd;
int incr;
@@ -580,7 +583,7 @@ block_insert(
// copy the new text
mch_memmove(newp + startcol, s, slen);
- offset += slen;
+ offset += (int)slen;
if (spaces > 0 && !bdp->is_short)
{
@@ -607,7 +610,7 @@ block_insert(
if (b_insert)
// correct any text properties
- inserted_bytes(lnum, startcol, slen);
+ inserted_bytes(lnum, startcol, (int)slen);
if (lnum == oap->end.lnum)
{
@@ -1258,8 +1261,8 @@ op_replace(oparg_T *oap, int c)
replace_character(c);
else
PBYTE(curwin->w_cursor, c);
- if (inc(&curwin->w_cursor) == -1)
- break;
+ if (inc(&curwin->w_cursor) == -1)
+ break;
}
}
@@ -1608,7 +1611,7 @@ op_insert(oparg_T *oap, long count1)
if (oap->block_mode)
{
- size_t ins_len;
+ int ins_len;
char_u *firstline, *ins_text;
struct block_def bd2;
int did_indent = FALSE;
@@ -1722,7 +1725,7 @@ op_insert(oparg_T *oap, long count1)
add = len; // short line, point to the NUL
firstline += add;
len -= add;
- if (pre_textlen >= 0 && (ins_len = len - pre_textlen - offset) > 0)
+ if (pre_textlen >= 0 && (ins_len = (int)len - pre_textlen - offset) > 0)
{
ins_text = vim_strnsave(firstline, ins_len);
if (ins_text != NULL)
@@ -1811,7 +1814,7 @@ op_change(oparg_T *oap)
*/
if (oap->block_mode && oap->start.lnum != oap->end.lnum && !got_int)
{
- size_t ins_len;
+ int ins_len;
// Auto-indenting may have changed the indent. If the cursor was past
// the indent, exclude that indent change from the inserted text.
@@ -1824,7 +1827,7 @@ op_change(oparg_T *oap)
bd.textcol += new_indent - pre_indent;
}
- ins_len = ml_get_len(oap->start.lnum) - pre_textlen;
+ ins_len = (int)ml_get_len(oap->start.lnum) - pre_textlen;
if (ins_len > 0)
{
// Subsequent calls to ml_get() flush the firstline data - take a
@@ -1866,7 +1869,7 @@ op_change(oparg_T *oap)
// Shift the properties for linenr as edit() would do.
if (curbuf->b_has_textprop)
adjust_prop_columns(linenr, bd.textcol,
- vpos.coladd + ins_len, 0);
+ vpos.coladd + (int)ins_len, 0);
#endif
}
}
@@ -2444,13 +2447,14 @@ charwise_block_prep(
int inclusive)
{
colnr_T startcol = 0, endcol = MAXCOL;
- int is_oneChar = FALSE;
colnr_T cs, ce;
char_u *p;
p = ml_get(lnum);
bdp->startspaces = 0;
bdp->endspaces = 0;
+ bdp->is_oneChar = FALSE;
+ bdp->start_char_vcols = 0;
if (lnum == start.lnum)
{
@@ -2462,8 +2466,8 @@ charwise_block_prep(
{
// Part of a tab selected -- but don't
// double-count it.
- bdp->startspaces = (ce - cs + 1)
- - start.coladd;
+ bdp->start_char_vcols = ce - cs + 1;
+ bdp->startspaces = bdp->start_char_vcols - start.coladd;
if (bdp->startspaces < 0)
bdp->startspaces = 0;
startcol++;
@@ -2483,19 +2487,16 @@ charwise_block_prep(
// of multi-byte char.
&& (*mb_head_off)(p, p + endcol) == 0))
{
- if (start.lnum == end.lnum
- && start.col == end.col)
+ if (start.lnum == end.lnum && start.col == end.col)
{
// Special case: inside a single char
- is_oneChar = TRUE;
- bdp->startspaces = end.coladd
- - start.coladd + inclusive;
+ bdp->is_oneChar = TRUE;
+ bdp->startspaces = end.coladd - start.coladd + inclusive;
endcol = startcol;
}
else
{
- bdp->endspaces = end.coladd
- + inclusive;
+ bdp->endspaces = end.coladd + inclusive;
endcol -= inclusive;
}
}
@@ -2503,10 +2504,11 @@ charwise_block_prep(
}
if (endcol == MAXCOL)
endcol = ml_get_len(lnum);
- if (startcol > endcol || is_oneChar)
+ if (startcol > endcol || bdp->is_oneChar)
bdp->textlen = 0;
else
bdp->textlen = endcol - startcol + inclusive;
+ bdp->textcol = startcol;
bdp->textstart = p + startcol;
}
@@ -2524,11 +2526,11 @@ op_addsub(
int change_cnt = 0;
linenr_T amount = Prenum1;
- // do_addsub() might trigger re-evaluation of 'foldexpr' halfway, when the
- // buffer is not completely updated yet. Postpone updating folds until before
- // the call to changed_lines().
+ // do_addsub() might trigger re-evaluation of 'foldexpr' halfway, when the
+ // buffer is not completely updated yet. Postpone updating folds until before
+ // the call to changed_lines().
#ifdef FEAT_FOLDING
- disable_fold_update++;
+ disable_fold_update++;
#endif
if (!VIsual_active)
diff --git a/src/option.c b/src/option.c
index 0cd2823..b26c606 100644
--- a/src/option.c
+++ b/src/option.c
@@ -417,6 +417,14 @@ set_init_xdg_rtp(void)
options[opt_idx].def_val[VI_DEFAULT] = xdg_rtp;
p_pp = xdg_rtp;
+#if defined(XDG_VDIR) && defined(FEAT_SESSION)
+ if ((opt_idx = findoption((char_u *)"viewdir")) < 0)
+ goto theend;
+
+ options[opt_idx].def_val[VI_DEFAULT] = (char_u *)XDG_VDIR;
+ p_vdir = (char_u *)XDG_VDIR;
+#endif
+
theend:
vim_free(vimrc1);
vim_free(vimrc2);
@@ -6216,6 +6224,10 @@ unset_global_local_option(char_u *name, void *from)
clear_string_option(&buf->b_p_inc);
break;
# endif
+ case PV_COT:
+ clear_string_option(&buf->b_p_cot);
+ buf->b_cot_flags = 0;
+ break;
case PV_DICT:
clear_string_option(&buf->b_p_dict);
break;
@@ -6325,6 +6337,7 @@ get_varp_scope(struct vimoption *p, int scope)
case PV_DEF: return (char_u *)&(curbuf->b_p_def);
case PV_INC: return (char_u *)&(curbuf->b_p_inc);
#endif
+ case PV_COT: return (char_u *)&(curbuf->b_p_cot);
case PV_DICT: return (char_u *)&(curbuf->b_p_dict);
case PV_TSR: return (char_u *)&(curbuf->b_p_tsr);
#ifdef FEAT_COMPL_FUNC
@@ -6405,6 +6418,8 @@ get_varp(struct vimoption *p)
case PV_INC: return *curbuf->b_p_inc != NUL
? (char_u *)&(curbuf->b_p_inc) : p->var;
#endif
+ case PV_COT: return *curbuf->b_p_cot != NUL
+ ? (char_u *)&(curbuf->b_p_cot) : p->var;
case PV_DICT: return *curbuf->b_p_dict != NUL
? (char_u *)&(curbuf->b_p_dict) : p->var;
case PV_TSR: return *curbuf->b_p_tsr != NUL
@@ -7197,6 +7212,8 @@ buf_copy_options(buf_T *buf, int flags)
COPY_OPT_SCTX(buf, BV_INEX);
# endif
#endif
+ buf->b_p_cot = empty_option;
+ buf->b_cot_flags = 0;
buf->b_p_dict = empty_option;
buf->b_p_tsr = empty_option;
#ifdef FEAT_COMPL_FUNC
@@ -8347,10 +8364,10 @@ get_sidescrolloff_value(void)
}
/*
- * Get the local or global value of 'backupcopy'.
+ * Get the local or global value of 'backupcopy' flags.
*/
unsigned int
-get_bkc_value(buf_T *buf)
+get_bkc_flags(buf_T *buf)
{
return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags;
}
@@ -8369,7 +8386,7 @@ get_flp_value(buf_T *buf)
#endif
/*
- * Get the local or global value of the 'virtualedit' flags.
+ * Get the local or global value of 'virtualedit' flags.
*/
unsigned int
get_ve_flags(void)
diff --git a/src/option.h b/src/option.h
index bf889e4..91d8d95 100644
--- a/src/option.h
+++ b/src/option.h
@@ -513,6 +513,20 @@ EXTERN int p_confirm; // 'confirm'
#endif
EXTERN int p_cp; // 'compatible'
EXTERN char_u *p_cot; // 'completeopt'
+EXTERN unsigned cot_flags; // flags from 'completeopt'
+// Keep in sync with p_cot_values in optionstr.c
+#define COT_MENU 0x001
+#define COT_MENUONE 0x002
+#define COT_ANY_MENU 0x003 // combination of menu flags
+#define COT_LONGEST 0x004 // FALSE: insert full match,
+ // TRUE: insert longest prefix
+#define COT_PREVIEW 0x008
+#define COT_POPUP 0x010
+#define COT_POPUPHIDDEN 0x020
+#define COT_ANY_PREVIEW 0x038 // combination of preview flags
+#define COT_NOINSERT 0x040 // FALSE: select & insert, TRUE: noinsert
+#define COT_NOSELECT 0x080 // FALSE: select & insert, TRUE: noselect
+#define COT_FUZZY 0x100 // TRUE: fuzzy match enabled
#ifdef BACKSLASH_IN_FILENAME
EXTERN char_u *p_csl; // 'completeslash'
#endif
@@ -1127,6 +1141,7 @@ enum
, BV_CMS
#endif
, BV_COM
+ , BV_COT
, BV_CPT
, BV_DICT
, BV_TSR
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 33e3165..617f3ce 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -50,6 +50,7 @@
# define PV_CMS OPT_BUF(BV_CMS)
#endif
#define PV_COM OPT_BUF(BV_COM)
+#define PV_COT OPT_BOTH(OPT_BUF(BV_COT))
#define PV_CPT OPT_BUF(BV_CPT)
#define PV_DICT OPT_BOTH(OPT_BUF(BV_DICT))
#define PV_TSR OPT_BOTH(OPT_BUF(BV_TSR))
@@ -300,7 +301,7 @@ struct vimoption
# define ISP_LATIN1 (char_u *)"@,161-255"
#endif
-# define HIGHLIGHT_INIT "8:SpecialKey,~:EndOfBuffer,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr,G:CursorLineSign,O:CursorLineFold,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm,Z:StatusLineTermNC,g:MsgArea"
+# define HIGHLIGHT_INIT "8:SpecialKey,~:EndOfBuffer,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr,G:CursorLineSign,O:CursorLineFold,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,k:PmenuMatch,<:PmenuMatchSel,[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm,Z:StatusLineTermNC,g:MsgArea"
// Default python version for pyx* commands
#if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
@@ -629,7 +630,7 @@ static struct vimoption options[] =
{"commentstring", "cms", P_STRING|P_ALLOCED|P_VI_DEF|P_CURSWANT,
#ifdef FEAT_FOLDING
(char_u *)&p_cms, PV_CMS, did_set_commentstring, NULL,
- {(char_u *)"/*%s*/", (char_u *)0L}
+ {(char_u *)"/* %s */", (char_u *)0L}
#else
(char_u *)NULL, PV_NONE, NULL, NULL,
{(char_u *)0L, (char_u *)0L}
@@ -654,7 +655,7 @@ static struct vimoption options[] =
#endif
SCTX_INIT},
{"completeopt", "cot", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
- (char_u *)&p_cot, PV_NONE, did_set_completeopt, expand_set_completeopt,
+ (char_u *)&p_cot, PV_COT, did_set_completeopt, expand_set_completeopt,
{(char_u *)"menu,preview", (char_u *)0L}
SCTX_INIT},
{"completepopup", "cpp", P_STRING|P_VI_DEF|P_COMMA|P_NODUP|P_COLON,
diff --git a/src/optionstr.c b/src/optionstr.c
index 45f126f..d722981 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -118,7 +118,7 @@ static char *(p_fdm_values[]) = {"manual", "expr", "marker", "indent", "syntax",
NULL};
static char *(p_fcl_values[]) = {"all", NULL};
#endif
-static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview", "popup", "popuphidden", "noinsert", "noselect", NULL};
+static char *(p_cot_values[]) = {"menu", "menuone", "longest", "preview", "popup", "popuphidden", "noinsert", "noselect", "fuzzy", NULL};
#ifdef BACKSLASH_IN_FILENAME
static char *(p_csl_values[]) = {"slash", "backslash", NULL};
#endif
@@ -144,6 +144,7 @@ didset_string_options(void)
(void)opt_strings_flags(p_cmp, p_cmp_values, &cmp_flags, TRUE);
(void)opt_strings_flags(p_bkc, p_bkc_values, &bkc_flags, TRUE);
(void)opt_strings_flags(p_bo, p_bo_values, &bo_flags, TRUE);
+ (void)opt_strings_flags(p_cot, p_cot_values, &cot_flags, TRUE);
#ifdef FEAT_SESSION
(void)opt_strings_flags(p_ssop, p_ssop_values, &ssop_flags, TRUE);
(void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, TRUE);
@@ -301,6 +302,7 @@ check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_lop);
check_string_option(&buf->b_p_ft);
check_string_option(&buf->b_p_cinw);
+ check_string_option(&buf->b_p_cot);
check_string_option(&buf->b_p_cpt);
#ifdef FEAT_COMPL_FUNC
check_string_option(&buf->b_p_cfu);
@@ -1601,10 +1603,21 @@ expand_set_complete(optexpand_T *args, int *numMatches, char_u ***matches)
char *
did_set_completeopt(optset_T *args UNUSED)
{
- if (check_opt_strings(p_cot, p_cot_values, TRUE) != OK)
+ char_u *cot = p_cot;
+ unsigned *flags = &cot_flags;
+
+ if (args->os_flags & OPT_LOCAL)
+ {
+ cot = curbuf->b_p_cot;
+ flags = &curbuf->b_cot_flags;
+ }
+
+ if (check_opt_strings(cot, p_cot_values, TRUE) != OK)
+ return e_invalid_argument;
+
+ if (opt_strings_flags(cot, p_cot_values, flags, TRUE) != OK)
return e_invalid_argument;
- completeopt_was_set();
return NULL;
}
diff --git a/src/os_unix.h b/src/os_unix.h
index 6efd8ce..99184ab 100644
--- a/src/os_unix.h
+++ b/src/os_unix.h
@@ -347,6 +347,8 @@ typedef struct dsc$descriptor DESC;
# define DFLT_VDIR "sys$login:vimfiles/view"
# else
# define DFLT_VDIR "$HOME/.vim/view" // default for 'viewdir'
+# define XDG_VDIR (mch_getenv("XDG_CONFIG_HOME") ? \
+ "$XDG_CONFIG_HOME/vim/view" : "~/.config/vim/view")
# endif
#endif
diff --git a/src/po/check.vim b/src/po/check.vim
index b0460f1..2ea4a38 100644
--- a/src/po/check.vim
+++ b/src/po/check.vim
@@ -226,8 +226,8 @@ elseif ctu
" endif
endif
-" Check that all lines are no longer than 80 chars
-let overlong = search('\%>80v', 'n')
+" Check that no lines are longer than 80 chars (except comments)
+let overlong = search('^[^#]\%>80v', 'n')
if overlong > 0
echomsg "Lines should be wrapped at 80 columns"
" TODO: make this an error
diff --git a/src/po/it.po b/src/po/it.po
index 8bdc5e3..fde1c23 100644
--- a/src/po/it.po
+++ b/src/po/it.po
@@ -14,7 +14,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-03-06 09:13+0100\n"
+"POT-Creation-Date: 2024-05-28 09:50+0200\n"
"PO-Revision-Date: 2024-03-06 15:00+0100\n"
"Last-Translator: Antonio Colombo <azc100@gmail.com>\n"
"Language-Team: Italian\n"
@@ -421,9 +421,6 @@ msgstr "Katakana"
msgid "Bopomofo"
msgstr "Bopomofo"
-msgid "Not enough memory to set references, garbage collection aborted!"
-msgstr "Memoria insufficiente per impostarlo, recupero memoria fallito!"
-
msgid ""
"\n"
"\tLast set from "
@@ -810,6 +807,9 @@ msgid_plural "+-%s%3ld lines: "
msgstr[0] "+-%s%3ld riga: "
msgstr[1] "+-%s%3ld righe: "
+msgid "Not enough memory to set references, garbage collection aborted!"
+msgstr "Memoria insufficiente per impostarlo, recupero memoria fallito!"
+
msgid "No match at cursor, finding next"
msgstr "Nessuna corrispondenza al cursore, cerco la prossima"
@@ -3419,6 +3419,9 @@ msgstr " II file vimrc utente: \""
msgid " 3rd user vimrc file: \""
msgstr " III file vimrc utente: \""
+msgid " 4th user vimrc file: \""
+msgstr " IV file vimrc utente: \""
+
msgid " user exrc file: \""
msgstr " file exrc utente: \""
@@ -6351,6 +6354,9 @@ msgstr ""
"E876: (Espressione regolare NFA) Non c'è spazio sufficiente a immagazzinare "
"l'intero NFA"
+msgid "E877: (NFA regexp) Invalid character class: %d"
+msgstr "E877: (Espressione regolare NFA) Classe di caratteri non valida: %d"
+
msgid "E878: (NFA regexp) Could not allocate memory for branch traversal!"
msgstr ""
"E878: (Espressione regolare NFA) Non posso allocare memoria per lo zigzag di "
@@ -7780,13 +7786,15 @@ msgid "E1330: Invalid type for object variable: %s"
msgstr "E1330: Tipo non valido per variabile Object: %s"
msgid ""
-"E1331: Public must be followed by \"var\" or \"static\" or \"final\" or "
+"E1331: public must be followed by \"var\" or \"static\" or \"final\" or "
+"\"const\""
+msgstr ""
+"E1331: public dev'essere seguito da \"var\" o \"static\" o \"final\" o "
"\"const\""
-msgstr "E1331: Public dev'essere seguito da \"var\" o \"static\" o \"const\""
-msgid "E1332: Public variable name cannot start with underscore: %s"
+msgid "E1332: public variable name cannot start with underscore: %s"
msgstr ""
-"E1332: Il nome di un elemento Pubblico non può iniziare con un trattino "
+"E1332: Il nome di un elemento public non può iniziare con il trattino "
"basso: %s"
msgid "E1333: Cannot access protected variable \"%s\" in class \"%s\""
@@ -7967,10 +7975,10 @@ msgid "E1386: Object method \"%s\" accessible only using class \"%s\" object"
msgstr ""
"E1386: Metodo Object \"%s\" accessibile solo usando la Classe \"%s\" object"
-msgid "E1387: Public variable not supported in an interface"
-msgstr "E1387: Variabile pubblica non supportata in un'Interfaccia"
+msgid "E1387: public variable not supported in an interface"
+msgstr "E1387: Variabile public non supportata in un'Interfaccia"
-msgid "E1388: Public keyword not supported for a method"
+msgid "E1388: public keyword not supported for a method"
msgstr "E1388: Attributo public non supportato per un Metodo"
msgid "E1389: Missing name after implements"
@@ -8054,6 +8062,54 @@ msgstr "E1412: Metodo Object predefinito \"%s\" non supportato"
msgid "E1413: Builtin class method not supported"
msgstr "E1413: Metodo Classe predefinito non supportato"
+msgid "E1414: Enum can only be defined in Vim9 script"
+msgstr "E1414: Enum può essere usato solo negli script Vim9"
+
+msgid "E1415: Enum name must start with an uppercase letter: %s"
+msgstr "E1415: Il nome di Enum deve iniziare con una lettera maiuscola: %s"
+
+msgid "E1416: Enum cannot extend a class or enum"
+msgstr "E1416: Enum non può estendere una Classe o un Enum"
+
+msgid "E1417: Abstract cannot be used in an Enum"
+msgstr "E1417: Impossibile usare Abstract in un Enum"
+
+msgid "E1418: Invalid enum value declaration: %s"
+msgstr "E1418: Dichiarazione di variabile Enum non valida: %s"
+
+msgid "E1419: Not a valid command in an Enum: %s"
+msgstr "E1419: Comando non valido in un Enum: %s"
+
+msgid "E1420: Missing :endenum"
+msgstr "E1420: Manca :endenum"
+
+msgid "E1421: Enum \"%s\" cannot be used as a value"
+msgstr "E1421: Impossibile usare come valore Enum \"%s\""
+
+msgid "E1422: Enum value \"%s\" not found in enum \"%s\""
+msgstr "E1422: Valore Enum \"%s\" non trovato in Enum \"%s\""
+
+msgid "E1423: Enum value \"%s.%s\" cannot be modified"
+msgstr "E1423: Valore Enum \"%s.%s\" non può essere modificato"
+
+msgid "E1424: Using an Enum \"%s\" as a Number"
+msgstr "E1424: Uso dell'Enum \"%s\" come un Numero"
+
+msgid "E1425: Using an Enum \"%s\" as a String"
+msgstr "E1425: Uso di un Enum \"%s\" come una Stringa"
+
+msgid "E1426: Enum \"%s\" ordinal value cannot be modified"
+msgstr "E1426: Il valore ordinale di Enum \"%s\" non può essere modificato"
+
+msgid "E1427: Enum \"%s\" name cannot be modified"
+msgstr "E1427: Il nome di Enum \"%s\" non può essere modificato"
+
+msgid "E1428: Duplicate enum value: %s"
+msgstr "E1428: Valore di Enum duplicato: %s"
+
+msgid "E1429: Class can only be used in a script"
+msgstr "E1429: Class si può usare solo in uno script"
+
msgid "E1500: Cannot mix positional and non-positional arguments: %s"
msgstr ""
"E1500: Non si possono mischiare argomenti posizionali e non posizionali: %s"
@@ -8101,8 +8157,9 @@ msgstr "E1511: Numero caratteri errato per campo \"%s\""
msgid "E1512: Wrong character width for field \"%s\""
msgstr "E1512: Larghezza carattere errata per campo \"%s\""
-msgid "E1513: Cannot edit buffer. 'winfixbuf' is enabled"
-msgstr "E1513: Non riesco a modificare il buffer. Opzione 'winfixbuf' attiva"
+msgid "E1513: Cannot switch buffer. 'winfixbuf' is enabled"
+msgstr "E1513: Non riesco a passare a un altro buffer. Opzione "
+"'winfixbuf' attiva"
#. type of cmdline window or 0
#. result of cmdline window or 0
diff --git a/src/popupmenu.c b/src/popupmenu.c
index 7419806..6e9d826 100644
--- a/src/popupmenu.c
+++ b/src/popupmenu.c
@@ -26,6 +26,9 @@ static int pum_base_width; // width of pum items base
static int pum_kind_width; // width of pum items kind column
static int pum_extra_width; // width of extra stuff
static int pum_scrollbar; // TRUE when scrollbar present
+#ifdef FEAT_RIGHTLEFT
+static int pum_rl; // TRUE when pum is drawn 'rightleft'
+#endif
static int pum_row; // top row of pum
static int pum_col; // left column of pum
@@ -101,8 +104,9 @@ pum_display(
#if defined(FEAT_QUICKFIX)
win_T *pvwin;
#endif
+
#ifdef FEAT_RIGHTLEFT
- int right_left = State == MODE_CMDLINE ? FALSE : curwin->w_p_rl;
+ pum_rl = State != MODE_CMDLINE && curwin->w_p_rl;
#endif
do
@@ -243,7 +247,7 @@ pum_display(
// w_wcol includes virtual text "above"
int wcol = curwin->w_wcol % curwin->w_width;
#ifdef FEAT_RIGHTLEFT
- if (right_left)
+ if (pum_rl)
cursor_col = curwin->w_wincol + curwin->w_width - wcol - 1;
else
#endif
@@ -264,8 +268,8 @@ pum_display(
if (((cursor_col < Columns - p_pw || cursor_col < Columns - max_width)
#ifdef FEAT_RIGHTLEFT
- && !right_left)
- || (right_left && (cursor_col > p_pw || cursor_col > max_width)
+ && !pum_rl)
+ || (pum_rl && (cursor_col > p_pw || cursor_col > max_width)
#endif
))
{
@@ -274,7 +278,7 @@ pum_display(
// start with the maximum space available
#ifdef FEAT_RIGHTLEFT
- if (right_left)
+ if (pum_rl)
pum_width = pum_col - pum_scrollbar + 1;
else
#endif
@@ -291,22 +295,22 @@ pum_display(
}
else if (((cursor_col > p_pw || cursor_col > max_width)
#ifdef FEAT_RIGHTLEFT
- && !right_left)
- || (right_left && (cursor_col < Columns - p_pw
+ && !pum_rl)
+ || (pum_rl && (cursor_col < Columns - p_pw
|| cursor_col < Columns - max_width)
#endif
))
{
// align pum edge with "cursor_col"
#ifdef FEAT_RIGHTLEFT
- if (right_left
+ if (pum_rl
&& W_ENDCOL(curwin) < max_width + pum_scrollbar + 1)
{
pum_col = cursor_col + max_width + pum_scrollbar + 1;
if (pum_col >= Columns)
pum_col = Columns - 1;
}
- else if (!right_left)
+ else if (!pum_rl)
#endif
{
if (curwin->w_wincol > Columns - max_width - pum_scrollbar
@@ -320,7 +324,7 @@ pum_display(
}
#ifdef FEAT_RIGHTLEFT
- if (right_left)
+ if (pum_rl)
pum_width = pum_col - pum_scrollbar + 1;
else
#endif
@@ -330,7 +334,7 @@ pum_display(
{
pum_width = p_pw;
#ifdef FEAT_RIGHTLEFT
- if (right_left)
+ if (pum_rl)
{
if (pum_width > pum_col)
pum_width = pum_col;
@@ -358,7 +362,7 @@ pum_display(
{
// not enough room, will use what we have
#ifdef FEAT_RIGHTLEFT
- if (right_left)
+ if (pum_rl)
pum_col = Columns - 1;
else
#endif
@@ -370,7 +374,7 @@ pum_display(
if (max_width > p_pw)
max_width = p_pw; // truncate
#ifdef FEAT_RIGHTLEFT
- if (right_left)
+ if (pum_rl)
pum_col = max_width - 1;
else
#endif
@@ -417,6 +421,119 @@ pum_under_menu(int row, int col, int only_redrawing)
}
/*
+ * Computes attributes of text on the popup menu.
+ * Returns attributes for every cell, or NULL if all attributes are the same.
+ */
+ static int *
+pum_compute_text_attrs(char_u *text, hlf_T hlf)
+{
+ int i;
+ size_t leader_len;
+ int char_cells;
+ int new_attr;
+ char_u *ptr = text;
+ int cell_idx = 0;
+ garray_T *ga = NULL;
+ int *attrs = NULL;
+ char_u *leader = NULL;
+ int in_fuzzy;
+ int matched_start = FALSE;
+ int_u char_pos = 0;
+
+ if ((hlf != HLF_PSI && hlf != HLF_PNI)
+ || (highlight_attr[HLF_PMSI] == highlight_attr[HLF_PSI]
+ && highlight_attr[HLF_PMNI] == highlight_attr[HLF_PNI]))
+ return NULL;
+
+ leader = State == MODE_CMDLINE ? cmdline_compl_pattern()
+ : ins_compl_leader();
+ if (leader == NULL || *leader == NUL)
+ return NULL;
+
+ attrs = ALLOC_MULT(int, vim_strsize(text));
+ if (attrs == NULL)
+ return NULL;
+
+ in_fuzzy = State == MODE_CMDLINE ? cmdline_compl_is_fuzzy()
+ : (get_cot_flags() & COT_FUZZY) != 0;
+ leader_len = STRLEN(leader);
+
+ if (in_fuzzy)
+ ga = fuzzy_match_str_with_pos(text, leader);
+ else
+ matched_start = MB_STRNICMP(text, leader, leader_len) == 0;
+
+ while (*ptr != NUL)
+ {
+ new_attr = highlight_attr[hlf];
+
+ if (ga != NULL)
+ {
+ // Handle fuzzy matching
+ for (i = 0; i < ga->ga_len; i++)
+ {
+ if (char_pos == ((int_u *)ga->ga_data)[i])
+ {
+ new_attr = highlight_attr[hlf == HLF_PSI
+ ? HLF_PMSI : HLF_PMNI];
+ break;
+ }
+ }
+ }
+ else if (matched_start && ptr < text + leader_len)
+ new_attr = highlight_attr[hlf == HLF_PSI ? HLF_PMSI : HLF_PMNI];
+
+ char_cells = mb_ptr2cells(ptr);
+ for (i = 0; i < char_cells; i++)
+ attrs[cell_idx + i] = new_attr;
+ cell_idx += char_cells;
+
+ MB_PTR_ADV(ptr);
+ char_pos++;
+ }
+
+ if (ga != NULL)
+ {
+ ga_clear(ga);
+ vim_free(ga);
+ }
+ return attrs;
+}
+
+/*
+ * Displays text on the popup menu with specific attributes.
+ */
+ static void
+pum_screen_puts_with_attrs(
+ int row,
+ int col,
+ int cells UNUSED,
+ char_u *text,
+ int textlen,
+ int *attrs)
+{
+ int col_start = col;
+ char_u *ptr = text;
+ int char_len;
+ int attr;
+
+ // Render text with proper attributes
+ while (*ptr != NUL && ptr < text + textlen)
+ {
+ char_len = mb_ptr2len(ptr);
+#ifdef FEAT_RIGHTLEFT
+ if (pum_rl)
+ attr = attrs[col_start + cells - col - 1];
+ else
+#endif
+ attr = attrs[col - col_start];
+ screen_puts_len(ptr, char_len, row, col, attr);
+ col += mb_ptr2cells(ptr);
+ ptr += char_len;
+ }
+}
+
+/*
* Redraw the popup menu, using "pum_first" and "pum_selected".
*/
void
@@ -426,8 +543,9 @@ pum_redraw(void)
int col;
int attr_scroll = highlight_attr[HLF_PSB];
int attr_thumb = highlight_attr[HLF_PST];
+ hlf_T *hlfs; // array used for highlights
+ hlf_T hlf;
int attr;
- int *attrs; // array used for highlights
int i;
int idx;
char_u *s;
@@ -438,17 +556,17 @@ pum_redraw(void)
int round;
int n;
- int attrsNorm[3];
- int attrsSel[3];
+ hlf_T hlfsNorm[3];
+ hlf_T hlfsSel[3];
// "word"
- attrsNorm[0] = highlight_attr[HLF_PNI];
- attrsSel[0] = highlight_attr[HLF_PSI];
+ hlfsNorm[0] = HLF_PNI;
+ hlfsSel[0] = HLF_PSI;
// "kind"
- attrsNorm[1] = highlight_attr[HLF_PNK];
- attrsSel[1] = highlight_attr[HLF_PSK];
+ hlfsNorm[1] = HLF_PNK;
+ hlfsSel[1] = HLF_PSK;
// "extra text"
- attrsNorm[2] = highlight_attr[HLF_PNX];
- attrsSel[2] = highlight_attr[HLF_PSX];
+ hlfsNorm[2] = HLF_PNX;
+ hlfsSel[2] = HLF_PSX;
if (call_update_screen)
{
@@ -483,12 +601,13 @@ pum_redraw(void)
for (i = 0; i < pum_height; ++i)
{
idx = i + pum_first;
- attrs = (idx == pum_selected) ? attrsSel : attrsNorm;
- attr = attrs[0]; // start with "word" highlight
+ hlfs = (idx == pum_selected) ? hlfsSel : hlfsNorm;
+ hlf = hlfs[0]; // start with "word" highlight
+ attr = highlight_attr[hlf];
// prepend a space if there is room
#ifdef FEAT_RIGHTLEFT
- if (curwin->w_p_rl)
+ if (pum_rl)
{
if (pum_col < curwin->w_wincol + curwin->w_width - 1)
screen_putchar(' ', row, pum_col + 1, attr);
@@ -507,7 +626,8 @@ pum_redraw(void)
totwidth = 0;
for (round = 0; round < 3; ++round)
{
- attr = attrs[round];
+ hlf = hlfs[round];
+ attr = highlight_attr[hlf];
width = 0;
s = NULL;
switch (round)
@@ -527,6 +647,7 @@ pum_redraw(void)
// Display the text that fits or comes before a Tab.
// First convert it to printable characters.
char_u *st;
+ int *attrs;
int saved = *p;
if (saved != NUL)
@@ -534,8 +655,11 @@ pum_redraw(void)
st = transstr(s);
if (saved != NUL)
*p = saved;
+
+ attrs = pum_compute_text_attrs(st, hlf);
+
#ifdef FEAT_RIGHTLEFT
- if (curwin->w_p_rl)
+ if (pum_rl)
{
if (st != NULL)
{
@@ -544,19 +668,19 @@ pum_redraw(void)
if (rt != NULL)
{
char_u *rt_start = rt;
- int size;
+ int cells;
- size = vim_strsize(rt);
- if (size > pum_width)
+ cells = vim_strsize(rt);
+ if (cells > pum_width)
{
do
{
- size -= has_mbyte
- ? (*mb_ptr2cells)(rt) : 1;
+ cells -= has_mbyte
+ ? (*mb_ptr2cells)(rt) : 1;
MB_PTR_ADV(rt);
- } while (size > pum_width);
+ } while (cells > pum_width);
- if (size < pum_width)
+ if (cells < pum_width)
{
// Most left character requires
// 2-cells but only 1 cell is
@@ -564,11 +688,18 @@ pum_redraw(void)
// '<' on the left of the pum
// item
*(--rt) = '<';
- size++;
+ cells++;
}
}
- screen_puts_len(rt, (int)STRLEN(rt),
- row, col - size + 1, attr);
+
+ if (attrs == NULL)
+ screen_puts_len(rt, (int)STRLEN(rt),
+ row, col - cells + 1, attr);
+ else
+ pum_screen_puts_with_attrs(row,
+ col - cells + 1, cells, rt,
+ (int)STRLEN(rt), attrs);
+
vim_free(rt_start);
}
vim_free(st);
@@ -596,18 +727,26 @@ pum_redraw(void)
else
--cells;
}
- screen_puts_len(st, size, row, col, attr);
+
+ if (attrs == NULL)
+ screen_puts_len(st, size, row, col, attr);
+ else
+ pum_screen_puts_with_attrs(row, col, cells,
+ st, size, attrs);
+
vim_free(st);
}
col += width;
}
+ vim_free(attrs);
+
if (*p != TAB)
break;
// Display two spaces for a Tab.
#ifdef FEAT_RIGHTLEFT
- if (curwin->w_p_rl)
+ if (pum_rl)
{
screen_puts_len((char_u *)" ", 2, row, col - 1,
attr);
@@ -640,11 +779,11 @@ pum_redraw(void)
|| pum_base_width + n >= pum_width)
break;
#ifdef FEAT_RIGHTLEFT
- if (curwin->w_p_rl)
+ if (pum_rl)
{
screen_fill(row, row + 1, pum_col - pum_base_width - n + 1,
col + 1, ' ', ' ', attr);
- col = pum_col - pum_base_width - n + 1;
+ col = pum_col - pum_base_width - n;
}
else
#endif
@@ -657,7 +796,7 @@ pum_redraw(void)
}
#ifdef FEAT_RIGHTLEFT
- if (curwin->w_p_rl)
+ if (pum_rl)
screen_fill(row, row + 1, pum_col - pum_width + 1, col + 1, ' ',
' ', attr);
else
@@ -667,7 +806,7 @@ pum_redraw(void)
if (pum_scrollbar > 0)
{
#ifdef FEAT_RIGHTLEFT
- if (curwin->w_p_rl)
+ if (pum_rl)
screen_putchar(' ', row, pum_col - pum_width,
i >= thumb_pos && i < thumb_pos + thumb_height
? attr_thumb : attr_scroll);
@@ -760,6 +899,7 @@ pum_set_selected(int n, int repeat UNUSED)
int context = pum_height / 2;
#ifdef FEAT_QUICKFIX
int prev_selected = pum_selected;
+ unsigned cur_cot_flags = get_cot_flags();
#endif
#if defined(FEAT_PROP_POPUP) && defined(FEAT_QUICKFIX)
int has_info = FALSE;
@@ -831,7 +971,7 @@ pum_set_selected(int n, int repeat UNUSED)
if (pum_array[pum_selected].pum_info != NULL
&& Rows > 10
&& repeat <= 1
- && vim_strchr(p_cot, 'p') != NULL)
+ && (cur_cot_flags & COT_ANY_PREVIEW))
{
win_T *curwin_save = curwin;
tabpage_T *curtab_save = curtab;
@@ -842,9 +982,9 @@ pum_set_selected(int n, int repeat UNUSED)
# endif
# ifdef FEAT_PROP_POPUP
has_info = TRUE;
- if (strstr((char *)p_cot, "popuphidden") != NULL)
+ if (cur_cot_flags & COT_POPUPHIDDEN)
use_popup = USEPOPUP_HIDDEN;
- else if (strstr((char *)p_cot, "popup") != NULL)
+ else if (cur_cot_flags & COT_POPUP)
use_popup = USEPOPUP_NORMAL;
else
use_popup = USEPOPUP_NONE;
@@ -1211,16 +1351,34 @@ pum_position_at_mouse(int min_width)
pum_row = 0;
}
}
- if (Columns - mouse_col >= pum_base_width
- || Columns - mouse_col > min_width)
- // Enough space to show at mouse column.
- pum_col = mouse_col;
+
+# ifdef FEAT_RIGHTLEFT
+ if (pum_rl)
+ {
+ if (mouse_col + 1 >= pum_base_width
+ || mouse_col + 1 > min_width)
+ // Enough space to show at mouse column.
+ pum_col = mouse_col;
+ else
+ // Not enough space, left align with window.
+ pum_col = (pum_base_width > min_width
+ ? min_width : pum_base_width) - 1;
+ pum_width = pum_col + 1;
+ }
else
- // Not enough space, right align with window.
- pum_col = Columns - (pum_base_width > min_width
+# endif
+ {
+ if (Columns - mouse_col >= pum_base_width
+ || Columns - mouse_col > min_width)
+ // Enough space to show at mouse column.
+ pum_col = mouse_col;
+ else
+ // Not enough space, right align with window.
+ pum_col = Columns - (pum_base_width > min_width
? min_width : pum_base_width);
+ pum_width = Columns - pum_col;
+ }
- pum_width = Columns - pum_col;
if (pum_width > pum_base_width + 1)
pum_width = pum_base_width + 1;
@@ -1444,6 +1602,9 @@ ui_post_balloon(char_u *mesg, list_T *list)
pum_compute_size();
pum_scrollbar = 0;
pum_height = balloon_arraysize;
+# ifdef FEAT_RIGHTLEFT
+ pum_rl = curwin->w_p_rl;
+# endif
pum_position_at_mouse(BALLOON_MIN_WIDTH);
pum_selected = -1;
@@ -1554,6 +1715,9 @@ pum_show_popupmenu(vimmenu_T *menu)
pum_compute_size();
pum_scrollbar = 0;
pum_height = pum_size;
+# ifdef FEAT_RIGHTLEFT
+ pum_rl = curwin->w_p_rl;
+# endif
pum_position_at_mouse(20);
pum_selected = -1;
@@ -1649,7 +1813,11 @@ pum_make_popup(char_u *path_name, int use_mouse_pos)
// Hack: set mouse position at the cursor so that the menu pops up
// around there.
mouse_row = W_WINROW(curwin) + curwin->w_wrow;
- mouse_col = curwin->w_wincol + curwin->w_wcol;
+ mouse_col =
+# ifdef FEAT_RIGHTLEFT
+ curwin->w_p_rl ? W_ENDCOL(curwin) - curwin->w_wcol - 1 :
+# endif
+ curwin->w_wincol + curwin->w_wcol;
}
menu = gui_find_menu(path_name);
diff --git a/src/popupwin.c b/src/popupwin.c
index 25bb153..38c1c9e 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -654,8 +654,8 @@ popup_show_curline(win_T *wp)
wp->w_topline = wp->w_buffer->b_ml.ml_line_count;
while (wp->w_topline < wp->w_cursor.lnum
&& wp->w_topline < wp->w_buffer->b_ml.ml_line_count
- && plines_m_win(wp, wp->w_topline, wp->w_cursor.lnum, FALSE)
- > wp->w_height)
+ && plines_m_win(wp, wp->w_topline, wp->w_cursor.lnum,
+ wp->w_height + 1) > wp->w_height)
++wp->w_topline;
}
diff --git a/src/proto.h b/src/proto.h
index 50802ce..94e34b0 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -94,6 +94,7 @@ extern int _stricoll(char *a, char *b);
# include "float.pro"
# include "fold.pro"
# include "getchar.pro"
+# include "gc.pro"
# include "gui_xim.pro"
# include "hardcopy.pro"
# include "hashtab.pro"
diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro
index 100c23d..dfa6d1b 100644
--- a/src/proto/cmdexpand.pro
+++ b/src/proto/cmdexpand.pro
@@ -6,6 +6,8 @@ int cmdline_pum_active(void);
void cmdline_pum_remove(void);
void cmdline_pum_cleanup(cmdline_info_T *cclp);
int cmdline_compl_startcol(void);
+char_u *cmdline_compl_pattern(void);
+int cmdline_compl_is_fuzzy(void);
char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode);
void ExpandInit(expand_T *xp);
void ExpandCleanup(expand_T *xp);
diff --git a/src/proto/cmdhist.pro b/src/proto/cmdhist.pro
index 9c9e56c..5bf4b8d 100644
--- a/src/proto/cmdhist.pro
+++ b/src/proto/cmdhist.pro
@@ -9,7 +9,7 @@ char_u *get_history_arg(expand_T *xp, int idx);
void init_history(void);
void clear_hist_entry(histentry_T *hisptr);
int in_history(int type, char_u *str, int move_to_front, int sep, int writing);
-void add_to_history(int histype, char_u *new_entry, int in_map, int sep);
+void add_to_history(int histype, char_u *new_entry, size_t new_entrylen, int in_map, int sep);
void f_histadd(typval_T *argvars, typval_T *rettv);
void f_histdel(typval_T *argvars, typval_T *rettv);
void f_histget(typval_T *argvars, typval_T *rettv);
diff --git a/src/proto/dict.pro b/src/proto/dict.pro
index fdccca5..346e1d5 100644
--- a/src/proto/dict.pro
+++ b/src/proto/dict.pro
@@ -37,6 +37,7 @@ varnumber_T dict_get_bool(dict_T *d, char *key, int def);
char_u *dict2string(typval_T *tv, int copyID, int restore_copyID);
char_u *get_literal_key(char_u **arg);
int eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal);
+int eval_lit_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg);
void dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name);
dictitem_T *dict_lookup(hashitem_T *hi);
int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive);
diff --git a/src/proto/eval.pro b/src/proto/eval.pro
index 1c2d05d..7247265 100644
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -48,19 +48,9 @@ int eval_addlist(typval_T *tv1, typval_T *tv2);
int eval_leader(char_u **arg, int vim9);
int handle_predefined(char_u *s, int len, typval_T *rettv);
int check_can_index(typval_T *rettv, int evaluate, int verbose);
-void f_slice(typval_T *argvars, typval_T *rettv);
int eval_index_inner(typval_T *rettv, int is_range, typval_T *var1, typval_T *var2, int exclusive, char_u *key, int keylen, int verbose);
char_u *partial_name(partial_T *pt);
void partial_unref(partial_T *pt);
-int get_copyID(void);
-int garbage_collect(int testing);
-int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack);
-int set_ref_in_dict(dict_T *d, int copyID);
-int set_ref_in_list(list_T *ll, int copyID);
-int set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack);
-int set_ref_in_callback(callback_T *cb, int copyID);
-int set_ref_in_item_class(class_T *cl, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack);
-int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack);
char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int composite_val);
char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID);
int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx);
diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro
index 3fd20b2..3bd7705 100644
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -66,8 +66,8 @@ void restore_current_state(save_state_T *sst);
void ex_normal(exarg_T *eap);
void exec_normal_cmd(char_u *cmd, int remap, int silent);
void exec_normal(int was_typed, int use_vpeekc, int may_use_terminal_loop);
-int find_cmdline_var(char_u *src, int *usedlen);
-char_u *eval_vars(char_u *src, char_u *srcstart, int *usedlen, linenr_T *lnump, char **errormsg, int *escaped, int empty_is_error);
+int find_cmdline_var(char_u *src, size_t *usedlen);
+char_u *eval_vars(char_u *src, char_u *srcstart, size_t *usedlen, linenr_T *lnump, char **errormsg, int *escaped, int empty_is_error);
char_u *expand_sfile(char_u *arg);
void dialog_msg(char_u *buff, char *format, char_u *fname);
void set_no_hlsearch(int flag);
diff --git a/src/proto/fileio.pro b/src/proto/fileio.pro
index 3f7b30d..8d97870 100644
--- a/src/proto/fileio.pro
+++ b/src/proto/fileio.pro
@@ -26,6 +26,7 @@ char_u *modname(char_u *fname, char_u *ext, int prepend_dot);
char_u *buf_modname(int shortname, char_u *fname, char_u *ext, int prepend_dot);
int vim_fgets(char_u *buf, int size, FILE *fp);
int vim_rename(char_u *from, char_u *to);
+int vim_copyfile(char_u *from, char_u *to);
int check_timestamps(int focus);
int buf_check_timestamp(buf_T *buf, int focus);
void buf_reload(buf_T *buf, int orig_mode, int reload_options);
diff --git a/src/proto/filepath.pro b/src/proto/filepath.pro
index fd8de80..46f51cb 100644
--- a/src/proto/filepath.pro
+++ b/src/proto/filepath.pro
@@ -1,11 +1,12 @@
/* filepath.c */
-int modify_fname(char_u *src, int tilde_file, int *usedlen, char_u **fnamep, char_u **bufp, int *fnamelen);
+int modify_fname(char_u *src, int tilde_file, size_t *usedlen, char_u **fnamep, char_u **bufp, int *fnamelen);
void shorten_dir(char_u *str);
int file_is_readable(char_u *fname);
void f_chdir(typval_T *argvars, typval_T *rettv);
void f_delete(typval_T *argvars, typval_T *rettv);
void f_executable(typval_T *argvars, typval_T *rettv);
void f_exepath(typval_T *argvars, typval_T *rettv);
+void f_filecopy(typval_T *argvars, typval_T *rettv);
void f_filereadable(typval_T *argvars, typval_T *rettv);
void f_filewritable(typval_T *argvars, typval_T *rettv);
void f_finddir(typval_T *argvars, typval_T *rettv);
diff --git a/src/proto/gc.pro b/src/proto/gc.pro
new file mode 100644
index 0000000..e13dbda
--- /dev/null
+++ b/src/proto/gc.pro
@@ -0,0 +1,12 @@
+/* gc.c */
+int get_copyID(void);
+int garbage_collect(int testing);
+int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack);
+int set_ref_in_dict(dict_T *d, int copyID);
+int set_ref_in_list(list_T *ll, int copyID);
+int set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack);
+int set_ref_in_callback(callback_T *cb, int copyID);
+int set_ref_in_item_class(class_T *cl, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack);
+int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack);
+/* vim: set ft=c : */
+
diff --git a/src/proto/indent.pro b/src/proto/indent.pro
index bafcefc..6e56a0e 100644
--- a/src/proto/indent.pro
+++ b/src/proto/indent.pro
@@ -1,15 +1,15 @@
/* indent.c */
int tabstop_set(char_u *var, int **array);
int tabstop_padding(colnr_T col, int ts_arg, int *vts);
-int tabstop_at(colnr_T col, int ts, int *vts);
+int tabstop_at(colnr_T col, int ts, int *vts, int left);
colnr_T tabstop_start(colnr_T col, int ts, int *vts);
void tabstop_fromto(colnr_T start_col, colnr_T end_col, int ts_arg, int *vts, int *ntabs, int *nspcs);
int *tabstop_copy(int *oldts);
int tabstop_count(int *ts);
int tabstop_first(int *ts);
long get_sw_value(buf_T *buf);
-long get_sw_value_indent(buf_T *buf);
-long get_sw_value_col(buf_T *buf, colnr_T col);
+long get_sw_value_indent(buf_T *buf, int left);
+long get_sw_value_col(buf_T *buf, colnr_T col, int left);
long get_sts_value(void);
int get_indent(void);
int get_indent_lnum(linenr_T lnum);
diff --git a/src/proto/insexpand.pro b/src/proto/insexpand.pro
index 51f5db0..dbd5ef7 100644
--- a/src/proto/insexpand.pro
+++ b/src/proto/insexpand.pro
@@ -27,9 +27,10 @@ int ins_compl_accept_char(int c);
int ins_compl_add_infercase(char_u *str_arg, int len, int icase, char_u *fname, int dir, int cont_s_ipos);
int ins_compl_has_shown_match(void);
int ins_compl_long_shown_match(void);
-void completeopt_was_set(void);
+unsigned int get_cot_flags(void);
int pum_wanted(void);
void ins_compl_show_pum(void);
+char_u *ins_compl_leader(void);
char_u *find_word_start(char_u *ptr);
char_u *find_word_end(char_u *ptr);
void ins_compl_clear(void);
diff --git a/src/proto/list.pro b/src/proto/list.pro
index 0b58c69..1659b8f 100644
--- a/src/proto/list.pro
+++ b/src/proto/list.pro
@@ -65,4 +65,5 @@ void f_insert(typval_T *argvars, typval_T *rettv);
void f_remove(typval_T *argvars, typval_T *rettv);
void f_reverse(typval_T *argvars, typval_T *rettv);
void f_reduce(typval_T *argvars, typval_T *rettv);
+void f_slice(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */
diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro
index e76bf75..d64f961 100644
--- a/src/proto/misc1.pro
+++ b/src/proto/misc1.pro
@@ -7,7 +7,7 @@ int plines_nofill(linenr_T lnum);
int plines_win_nofill(win_T *wp, linenr_T lnum, int limit_winheight);
int plines_win_nofold(win_T *wp, linenr_T lnum);
int plines_win_col(win_T *wp, linenr_T lnum, long column);
-int plines_m_win(win_T *wp, linenr_T first, linenr_T last, int limit_winheight);
+int plines_m_win(win_T *wp, linenr_T first, linenr_T last, int max);
int gchar_pos(pos_T *pos);
int gchar_cursor(void);
void pchar_cursor(int c);
diff --git a/src/proto/normal.pro b/src/proto/normal.pro
index 6dcbe41..36a26ec 100644
--- a/src/proto/normal.pro
+++ b/src/proto/normal.pro
@@ -31,5 +31,6 @@ int get_visual_text(cmdarg_T *cap, char_u **pp, int *lenp);
void start_selection(void);
void may_start_select(int c);
int unadjust_for_sel(void);
+int unadjust_for_sel_inner(pos_T *pp);
void set_cursor_for_append_to_line(void);
/* vim: set ft=c : */
diff --git a/src/proto/option.pro b/src/proto/option.pro
index be7ee95..69463d4 100644
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -139,7 +139,7 @@ int reset_option_was_set(char_u *name);
int can_bs(int what);
long get_scrolloff_value(void);
long get_sidescrolloff_value(void);
-unsigned int get_bkc_value(buf_T *buf);
+unsigned int get_bkc_flags(buf_T *buf);
char_u *get_flp_value(buf_T *buf);
unsigned int get_ve_flags(void);
char_u *get_showbreak_value(win_T *win);
diff --git a/src/proto/search.pro b/src/proto/search.pro
index 5b2b889..08526c8 100644
--- a/src/proto/search.pro
+++ b/src/proto/search.pro
@@ -1,7 +1,7 @@
/* search.c */
-int search_regcomp(char_u *pat, char_u **used_pat, int pat_save, int pat_use, int options, regmmatch_T *regmatch);
+int search_regcomp(char_u *pat, size_t patlen, char_u **used_pat, int pat_save, int pat_use, int options, regmmatch_T *regmatch);
char_u *get_search_pat(void);
-void save_re_pat(int idx, char_u *pat, int magic);
+void save_re_pat(int idx, char_u *pat, size_t patlen, int magic);
void save_search_patterns(void);
void restore_search_patterns(void);
void free_search_patterns(void);
@@ -21,9 +21,9 @@ char_u *last_search_pat(void);
void reset_search_dir(void);
void set_last_search_pat(char_u *s, int idx, int magic, int setlast);
void last_pat_prog(regmmatch_T *regmatch);
-int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, int dir, char_u *pat, long count, int options, int pat_use, searchit_arg_T *extra_arg);
+int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, int dir, char_u *pat, size_t patlen, long count, int options, int pat_use, searchit_arg_T *extra_arg);
void set_search_direction(int cdir);
-int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, int options, searchit_arg_T *sia);
+int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, size_t patlen, long count, int options, searchit_arg_T *sia);
int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat);
int searchc(cmdarg_T *cap, int t_cmd);
pos_T *findmatch(oparg_T *oap, int initc);
@@ -40,6 +40,7 @@ int fuzzy_match(char_u *str, char_u *pat_arg, int matchseq, int *outScore, int_u
void f_matchfuzzy(typval_T *argvars, typval_T *rettv);
void f_matchfuzzypos(typval_T *argvars, typval_T *rettv);
int fuzzy_match_str(char_u *str, char_u *pat);
+garray_T *fuzzy_match_str_with_pos(char_u *str, char_u *pat);
void fuzmatch_str_free(fuzmatch_str_T *fuzmatch, int count);
int fuzzymatches_to_strmatches(fuzmatch_str_T *fuzmatch, char_u ***matches, int count, int funcsort);
/* vim: set ft=c : */
diff --git a/src/proto/spell.pro b/src/proto/spell.pro
index 98a1353..680bf34 100644
--- a/src/proto/spell.pro
+++ b/src/proto/spell.pro
@@ -6,7 +6,7 @@ int match_compoundrule(slang_T *slang, char_u *compflags);
int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word, slang_T *slang, int cond_req);
int spell_valid_case(int wordflags, int treeflags);
int spell_check_window(win_T *wp);
-int spell_move_to(win_T *wp, int dir, int allwords, int curline, hlf_T *attrp);
+int spell_move_to(win_T *wp, int dir, smt_T behaviour, int curline, hlf_T *attrp);
void spell_cat_line(char_u *buf, char_u *line, int maxlen);
char_u *spell_enc(void);
slang_T *slang_alloc(char_u *lang);
diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro
index d3d3b99..1b5800c 100644
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -40,7 +40,7 @@ int is_class_name(char_u *name, typval_T *rettv);
void protected_method_access_errmsg(char_u *method_name);
int object_empty(object_T *obj);
int object_len(object_T *obj);
-char_u *object_string(object_T *obj, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int composite_val);
+char_u *object2string(object_T *obj, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int composite_val);
int class_instance_of(class_T *cl, class_T *other_cl);
void f_instanceof(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */
diff --git a/src/quickfix.c b/src/quickfix.c
index 2e5b693..414fe65 100644
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -3388,7 +3388,7 @@ qf_jump_goto_line(
// Move the cursor to the first line in the buffer
save_cursor = curwin->w_cursor;
curwin->w_cursor.lnum = 0;
- if (!do_search(NULL, '/', '/', qf_pattern, (long)1, SEARCH_KEEP, NULL))
+ if (!do_search(NULL, '/', '/', qf_pattern, STRLEN(qf_pattern), (long)1, SEARCH_KEEP, NULL))
curwin->w_cursor = save_cursor;
}
}
@@ -4867,6 +4867,9 @@ qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int qf_winid)
if (old_last == NULL)
{
+ win_T *wp;
+ tabpage_T *tp;
+
if (buf != curbuf)
{
internal_error("qf_fill_buffer()");
@@ -4883,6 +4886,10 @@ qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int qf_winid)
while ((curbuf->b_ml.ml_flags & ML_EMPTY) == 0)
(void)ml_delete((linenr_T)1);
+ FOR_ALL_TAB_WINDOWS(tp, wp)
+ if (wp->w_buffer == curbuf)
+ wp->w_skipcol = 0;
+
// Remove all undo information
u_clearallandblockfree(curbuf);
}
diff --git a/src/regexp.c b/src/regexp.c
index 4373ae0..ff201d9 100644
--- a/src/regexp.c
+++ b/src/regexp.c
@@ -161,6 +161,7 @@ re_multi_type(int c)
}
static char_u *reg_prev_sub = NULL;
+static size_t reg_prev_sublen = 0;
/*
* REGEXP_INRANGE contains all characters which are always special in a []
@@ -197,6 +198,30 @@ backslash_trans(int c)
return c;
}
+enum
+{
+ CLASS_ALNUM = 0,
+ CLASS_ALPHA,
+ CLASS_BLANK,
+ CLASS_CNTRL,
+ CLASS_DIGIT,
+ CLASS_GRAPH,
+ CLASS_LOWER,
+ CLASS_PRINT,
+ CLASS_PUNCT,
+ CLASS_SPACE,
+ CLASS_UPPER,
+ CLASS_XDIGIT,
+ CLASS_TAB,
+ CLASS_RETURN,
+ CLASS_BACKSPACE,
+ CLASS_ESCAPE,
+ CLASS_IDENT,
+ CLASS_KEYWORD,
+ CLASS_FNAME,
+ CLASS_NONE = 99
+};
+
/*
* Check for a character class name "[:name:]". "pp" points to the '['.
* Returns one of the CLASS_ items. CLASS_NONE means that no item was
@@ -205,58 +230,56 @@ backslash_trans(int c)
static int
get_char_class(char_u **pp)
{
- static const char *(class_names[]) =
+ // must be sorted by the 'value' field because it is used by bsearch()!
+ static keyvalue_T char_class_tab[] =
{
- "alnum:]",
-#define CLASS_ALNUM 0
- "alpha:]",
-#define CLASS_ALPHA 1
- "blank:]",
-#define CLASS_BLANK 2
- "cntrl:]",
-#define CLASS_CNTRL 3
- "digit:]",
-#define CLASS_DIGIT 4
- "graph:]",
-#define CLASS_GRAPH 5
- "lower:]",
-#define CLASS_LOWER 6
- "print:]",
-#define CLASS_PRINT 7
- "punct:]",
-#define CLASS_PUNCT 8
- "space:]",
-#define CLASS_SPACE 9
- "upper:]",
-#define CLASS_UPPER 10
- "xdigit:]",
-#define CLASS_XDIGIT 11
- "tab:]",
-#define CLASS_TAB 12
- "return:]",
-#define CLASS_RETURN 13
- "backspace:]",
-#define CLASS_BACKSPACE 14
- "escape:]",
-#define CLASS_ESCAPE 15
- "ident:]",
-#define CLASS_IDENT 16
- "keyword:]",
-#define CLASS_KEYWORD 17
- "fname:]",
-#define CLASS_FNAME 18
+ KEYVALUE_ENTRY(CLASS_ALNUM, "alnum:]"),
+ KEYVALUE_ENTRY(CLASS_ALPHA, "alpha:]"),
+ KEYVALUE_ENTRY(CLASS_BACKSPACE, "backspace:]"),
+ KEYVALUE_ENTRY(CLASS_BLANK, "blank:]"),
+ KEYVALUE_ENTRY(CLASS_CNTRL, "cntrl:]"),
+ KEYVALUE_ENTRY(CLASS_DIGIT, "digit:]"),
+ KEYVALUE_ENTRY(CLASS_ESCAPE, "escape:]"),
+ KEYVALUE_ENTRY(CLASS_FNAME, "fname:]"),
+ KEYVALUE_ENTRY(CLASS_GRAPH, "graph:]"),
+ KEYVALUE_ENTRY(CLASS_IDENT, "ident:]"),
+ KEYVALUE_ENTRY(CLASS_KEYWORD, "keyword:]"),
+ KEYVALUE_ENTRY(CLASS_LOWER, "lower:]"),
+ KEYVALUE_ENTRY(CLASS_PRINT, "print:]"),
+ KEYVALUE_ENTRY(CLASS_PUNCT, "punct:]"),
+ KEYVALUE_ENTRY(CLASS_RETURN, "return:]"),
+ KEYVALUE_ENTRY(CLASS_SPACE, "space:]"),
+ KEYVALUE_ENTRY(CLASS_TAB, "tab:]"),
+ KEYVALUE_ENTRY(CLASS_UPPER, "upper:]"),
+ KEYVALUE_ENTRY(CLASS_XDIGIT, "xdigit:]")
};
-#define CLASS_NONE 99
- int i;
- if ((*pp)[1] == ':')
+ // check that the value of "pp" has a chance of matching
+ if ((*pp)[1] == ':' && ASCII_ISLOWER((*pp)[2])
+ && ASCII_ISLOWER((*pp)[3]) && ASCII_ISLOWER((*pp)[4]))
{
- for (i = 0; i < (int)ARRAY_LENGTH(class_names); ++i)
- if (STRNCMP(*pp + 2, class_names[i], STRLEN(class_names[i])) == 0)
- {
- *pp += STRLEN(class_names[i]) + 2;
- return i;
- }
+ keyvalue_T target;
+ keyvalue_T *entry;
+ // this function can be called repeatedly with the same value for "pp"
+ // so we cache the last found entry.
+ static keyvalue_T *last_entry = NULL;
+
+ target.key = 0;
+ target.value = (char *)*pp + 2;
+ target.length = 0; // not used, see cmp_keyvalue_value_n()
+
+ if (last_entry != NULL && cmp_keyvalue_value_n(&target, last_entry) == 0)
+ entry = last_entry;
+ else
+ entry = (keyvalue_T *)bsearch(&target, &char_class_tab,
+ ARRAY_LENGTH(char_class_tab),
+ sizeof(char_class_tab[0]), cmp_keyvalue_value_n);
+ if (entry != NULL)
+ {
+ last_entry = entry;
+ *pp += entry->length + 2;
+ return entry->key;
+ }
}
return CLASS_NONE;
}
@@ -597,6 +620,7 @@ skip_regexp_ex(
{
magic_T mymagic;
char_u *p = startp;
+ size_t startplen = 0;
if (magic)
mymagic = MAGIC_ON;
@@ -620,16 +644,21 @@ skip_regexp_ex(
if (dirc == '?' && newp != NULL && p[1] == '?')
{
// change "\?" to "?", make a copy first.
+ if (startplen == 0)
+ startplen = STRLEN(startp);
if (*newp == NULL)
{
- *newp = vim_strsave(startp);
+ *newp = vim_strnsave(startp, startplen);
if (*newp != NULL)
+ {
p = *newp + (p - startp);
+ startp = *newp;
+ }
}
if (dropped != NULL)
++*dropped;
if (*newp != NULL)
- STRMOVE(p, p + 1);
+ mch_memmove(p, p + 1, startplen - ((p + 1) - startp) + 1);
else
++p;
}
@@ -1189,20 +1218,114 @@ reg_iswordc(int c)
return vim_iswordc_buf(c, rex.reg_buf);
}
+#ifdef FEAT_EVAL
+static int can_f_submatch = FALSE; // TRUE when submatch() can be used
+
+// This struct is used for reg_submatch(). Needed for when the
+// substitution string is an expression that contains a call to substitute()
+// and submatch().
+typedef struct {
+ regmatch_T *sm_match;
+ regmmatch_T *sm_mmatch;
+ linenr_T sm_firstlnum;
+ linenr_T sm_maxline;
+ int sm_line_lbr;
+} regsubmatch_T;
+
+static regsubmatch_T rsm; // can only be used when can_f_submatch is TRUE
+#endif
+
+typedef enum
+{
+ RGLF_LINE = 0x01,
+ RGLF_LENGTH = 0x02
+#ifdef FEAT_EVAL
+ ,
+ RGLF_SUBMATCH = 0x04
+#endif
+} reg_getline_flags_T;
+
+//
+// common code for reg_getline(), reg_getline_len(), reg_getline_submatch() and
+// reg_getline_submatch_len().
+// the flags argument (which is a bitmask) controls what info is to be returned and whether
+// or not submatch is in effect.
+// note:
+// submatch is available only if FEAT_EVAL is defined.
+ static void
+reg_getline_common(linenr_T lnum, reg_getline_flags_T flags, char_u **line, colnr_T *length)
+{
+ int get_line = flags & RGLF_LINE;
+ int get_length = flags & RGLF_LENGTH;
+ linenr_T firstlnum;
+ linenr_T maxline;
+
+#ifdef FEAT_EVAL
+ if (flags & RGLF_SUBMATCH)
+ {
+ firstlnum = rsm.sm_firstlnum + lnum;
+ maxline = rsm.sm_maxline;
+ }
+ else
+#endif
+ {
+ firstlnum = rex.reg_firstlnum + lnum;
+ maxline = rex.reg_maxline;
+ }
+
+ // when looking behind for a match/no-match lnum is negative. but we
+ // can't go before line 1.
+ if (firstlnum < 1)
+ {
+ if (get_line)
+ *line = NULL;
+ if (get_length)
+ *length = 0;
+
+ return;
+ }
+
+ if (lnum > maxline)
+ {
+ // must have matched the "\n" in the last line.
+ if (get_line)
+ *line = (char_u *)"";
+ if (get_length)
+ *length = 0;
+
+ return;
+ }
+
+ if (get_line)
+ *line = ml_get_buf(rex.reg_buf, firstlnum, FALSE);
+ if (get_length)
+ *length = ml_get_buf_len(rex.reg_buf, firstlnum);
+}
+
/*
* Get pointer to the line "lnum", which is relative to "reg_firstlnum".
*/
static char_u *
reg_getline(linenr_T lnum)
{
- // when looking behind for a match/no-match lnum is negative. But we
- // can't go before line 1
- if (rex.reg_firstlnum + lnum < 1)
- return NULL;
- if (lnum > rex.reg_maxline)
- // Must have matched the "\n" in the last line.
- return (char_u *)"";
- return ml_get_buf(rex.reg_buf, rex.reg_firstlnum + lnum, FALSE);
+ char_u *line;
+
+ reg_getline_common(lnum, RGLF_LINE, &line, NULL);
+
+ return line;
+}
+
+/*
+ * Get length of line "lnum", which is relative to "reg_firstlnum".
+ */
+ static colnr_T
+reg_getline_len(linenr_T lnum)
+{
+ colnr_T length;
+
+ reg_getline_common(lnum, RGLF_LENGTH, NULL, &length);
+
+ return length;
}
#ifdef FEAT_SYN_HL
@@ -1484,7 +1607,7 @@ match_with_backref(
if (clnum == end_lnum)
len = end_col - ccol;
else
- len = (int)STRLEN(p + ccol);
+ len = (int)reg_getline_len(clnum) - ccol;
if (cstrncmp(p + ccol, rex.input, &len) != 0)
return RA_NOMATCH; // doesn't match
@@ -1745,49 +1868,71 @@ regtilde(char_u *source, int magic)
{
char_u *newsub = source;
char_u *p;
+ size_t newsublen = 0;
+ char_u tilde[3] = {'~', NUL, NUL};
+ size_t tildelen = 1;
+ int error = FALSE;
+
+ if (!magic)
+ {
+ tilde[0] = '\\';
+ tilde[1] = '~';
+ tilde[2] = NUL;
+ tildelen = 2;
+ }
for (p = newsub; *p; ++p)
{
- if ((*p == '~' && magic) || (*p == '\\' && *(p + 1) == '~' && !magic))
+ if (STRNCMP(p, tilde, tildelen) == 0)
{
- if (reg_prev_sub != NULL)
+ size_t prefixlen = p - newsub; // not including the tilde
+ char_u *postfix = p + tildelen;
+ size_t postfixlen;
+ size_t tmpsublen;
+
+ if (newsublen == 0)
+ newsublen = STRLEN(newsub);
+ newsublen -= tildelen;
+ postfixlen = newsublen - prefixlen;
+ tmpsublen = prefixlen + reg_prev_sublen + postfixlen;
+
+ if (tmpsublen > 0 && reg_prev_sub != NULL)
{
- // length = len(newsub) - 1 + len(prev_sub) + 1
+ char_u *tmpsub;
+
// Avoid making the text longer than MAXCOL, it will cause
// trouble at some point.
- size_t prevsublen = STRLEN(reg_prev_sub);
- size_t newsublen = STRLEN(newsub);
- if (prevsublen > MAXCOL || newsublen > MAXCOL
- || newsublen + prevsublen > MAXCOL)
+ if (tmpsublen > MAXCOL)
{
emsg(_(e_resulting_text_too_long));
+ error = TRUE;
break;
}
- char_u *tmpsub = alloc(newsublen + prevsublen);
- if (tmpsub != NULL)
+ tmpsub = alloc(tmpsublen + 1);
+ if (tmpsub == NULL)
{
- // copy prefix
- size_t prefixlen = p - newsub; // not including ~
- mch_memmove(tmpsub, newsub, prefixlen);
- // interpret tilde
- mch_memmove(tmpsub + prefixlen, reg_prev_sub,
- prevsublen);
- // copy postfix
- if (!magic)
- ++p; // back off backslash
- STRCPY(tmpsub + prefixlen + prevsublen, p + 1);
-
- if (newsub != source) // allocated newsub before
- vim_free(newsub);
- newsub = tmpsub;
- p = newsub + prefixlen + prevsublen;
+ emsg(_(e_out_of_memory));
+ error = TRUE;
+ break;
}
+
+ // copy prefix
+ mch_memmove(tmpsub, newsub, prefixlen);
+ // interpret tilde
+ mch_memmove(tmpsub + prefixlen, reg_prev_sub, reg_prev_sublen);
+ // copy postfix
+ STRCPY(tmpsub + prefixlen + reg_prev_sublen, postfix);
+
+ if (newsub != source) // allocated newsub before
+ vim_free(newsub);
+ newsub = tmpsub;
+ newsublen = tmpsublen;
+ p = newsub + prefixlen + reg_prev_sublen;
}
- else if (magic)
- STRMOVE(p, p + 1); // remove '~'
else
- STRMOVE(p, p + 2); // remove '\~'
+ mch_memmove(p, postfix, postfixlen + 1); // remove the tilde (+1 for the NUL)
+
--p;
}
else
@@ -1799,32 +1944,34 @@ regtilde(char_u *source, int magic)
}
}
+ if (error)
+ {
+ if (newsub != source)
+ vim_free(newsub);
+ return source;
+ }
+
// Store a copy of newsub in reg_prev_sub. It is always allocated,
// because recursive calls may make the returned string invalid.
- vim_free(reg_prev_sub);
- reg_prev_sub = vim_strsave(newsub);
+ // Only store it if there something to store.
+ newsublen = p - newsub;
+ if (newsublen == 0)
+ VIM_CLEAR(reg_prev_sub);
+ else
+ {
+ vim_free(reg_prev_sub);
+ reg_prev_sub = vim_strnsave(newsub, newsublen);
+ }
+
+ if (reg_prev_sub == NULL)
+ reg_prev_sublen = 0;
+ else
+ reg_prev_sublen = newsublen;
return newsub;
}
#ifdef FEAT_EVAL
-static int can_f_submatch = FALSE; // TRUE when submatch() can be used
-
-// These pointers are used for reg_submatch(). Needed for when the
-// substitution string is an expression that contains a call to substitute()
-// and submatch().
-typedef struct {
- regmatch_T *sm_match;
- regmmatch_T *sm_mmatch;
- linenr_T sm_firstlnum;
- linenr_T sm_maxline;
- int sm_line_lbr;
-} regsubmatch_T;
-
-static regsubmatch_T rsm; // can only be used when can_f_submatch is TRUE
-#endif
-
-#ifdef FEAT_EVAL
/*
* Put the submatches in "argv[argskip]" which is a list passed into
@@ -2028,12 +2175,16 @@ vim_regsub_both(
// "flags & REGSUB_COPY" != 0.
if (copy)
{
- if (eval_result[nested] != NULL &&
- (int)STRLEN(eval_result[nested]) < destlen)
+ if (eval_result[nested] != NULL)
{
- STRCPY(dest, eval_result[nested]);
- dst += STRLEN(eval_result[nested]);
- VIM_CLEAR(eval_result[nested]);
+ int eval_len = (int)STRLEN(eval_result[nested]);
+
+ if (eval_len < destlen)
+ {
+ STRCPY(dest, eval_result[nested]);
+ dst += eval_len;
+ VIM_CLEAR(eval_result[nested]);
+ }
}
}
else
@@ -2325,7 +2476,7 @@ vim_regsub_both(
len = rex.reg_mmatch->endpos[no].col
- rex.reg_mmatch->startpos[no].col;
else
- len = (int)STRLEN(s);
+ len = (int)reg_getline_len(clnum) - rex.reg_mmatch->startpos[no].col;
}
}
else
@@ -2360,7 +2511,7 @@ vim_regsub_both(
if (rex.reg_mmatch->endpos[no].lnum == clnum)
len = rex.reg_mmatch->endpos[no].col;
else
- len = (int)STRLEN(s);
+ len = (int)reg_getline_len(clnum);
}
else
break;
@@ -2465,26 +2616,25 @@ exit:
}
#ifdef FEAT_EVAL
-/*
- * Call reg_getline() with the line numbers from the submatch. If a
- * substitute() was used the reg_maxline and other values have been
- * overwritten.
- */
+
static char_u *
reg_getline_submatch(linenr_T lnum)
{
- char_u *s;
- linenr_T save_first = rex.reg_firstlnum;
- linenr_T save_max = rex.reg_maxline;
+ char_u *line;
+
+ reg_getline_common(lnum, RGLF_LINE | RGLF_SUBMATCH, &line, NULL);
+
+ return line;
+}
- rex.reg_firstlnum = rsm.sm_firstlnum;
- rex.reg_maxline = rsm.sm_maxline;
+ static colnr_T
+reg_getline_submatch_len(linenr_T lnum)
+{
+ colnr_T length;
- s = reg_getline(lnum);
+ reg_getline_common(lnum, RGLF_LENGTH | RGLF_SUBMATCH, NULL, &length);
- rex.reg_firstlnum = save_first;
- rex.reg_maxline = save_max;
- return s;
+ return length;
}
/*
@@ -2533,7 +2683,7 @@ reg_submatch(int no)
{
// Multiple lines: take start line from start col, middle
// lines completely and end line up to end col.
- len = (int)STRLEN(s);
+ len = (int)reg_getline_submatch_len(lnum) - rsm.sm_mmatch->startpos[no].col;
if (round == 2)
{
STRCPY(retval, s);
@@ -2543,13 +2693,14 @@ reg_submatch(int no)
++lnum;
while (lnum < rsm.sm_mmatch->endpos[no].lnum)
{
- s = reg_getline_submatch(lnum++);
+ s = reg_getline_submatch(lnum);
if (round == 2)
STRCPY(retval + len, s);
- len += (int)STRLEN(s);
+ len += (int)reg_getline_submatch_len(lnum);
if (round == 2)
retval[len] = '\n';
++len;
+ ++lnum;
}
if (round == 2)
STRNCPY(retval + len, reg_getline_submatch(lnum),
@@ -2624,9 +2775,11 @@ reg_submatch_list(int no)
}
else
{
+ int max_lnum = elnum - slnum;
+
if (list_append_string(list, s, -1) == FAIL)
error = TRUE;
- for (i = 1; i < elnum - slnum; i++)
+ for (i = 1; i < max_lnum; i++)
{
s = reg_getline_submatch(slnum + i);
if (list_append_string(list, s, -1) == FAIL)
diff --git a/src/regexp_bt.c b/src/regexp_bt.c
index 5d9450d..5452dda 100644
--- a/src/regexp_bt.c
+++ b/src/regexp_bt.c
@@ -2564,14 +2564,22 @@ bt_regcomp(char_u *expr, int re_flags)
if ((flags & SPSTART || OP(scan) == BOW || OP(scan) == EOW)
&& !(flags & HASNL))
{
+ size_t scanlen;
+
longest = NULL;
len = 0;
for (; scan != NULL; scan = regnext(scan))
- if (OP(scan) == EXACTLY && STRLEN(OPERAND(scan)) >= (size_t)len)
+ {
+ if (OP(scan) == EXACTLY)
{
- longest = OPERAND(scan);
- len = (int)STRLEN(OPERAND(scan));
+ scanlen = STRLEN(OPERAND(scan));
+ if (scanlen >= (size_t)len)
+ {
+ longest = OPERAND(scan);
+ len = (int)scanlen;
+ }
}
+ }
r->regmust = longest;
r->regmlen = len;
}
@@ -3406,8 +3414,7 @@ regmatch(
{
colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum
&& pos->col == MAXCOL
- ? (colnr_T)STRLEN(reg_getline(
- pos->lnum - rex.reg_firstlnum))
+ ? reg_getline_len(pos->lnum - rex.reg_firstlnum)
: pos->col;
if ((pos->lnum == rex.lnum + rex.reg_firstlnum
@@ -4695,7 +4702,7 @@ regmatch(
// right.
if (rex.line == NULL)
break;
- rex.input = rex.line + STRLEN(rex.line);
+ rex.input = rex.line + reg_getline_len(rex.lnum);
fast_breakcheck();
}
else
@@ -5249,8 +5256,10 @@ regprop(char_u *op)
{
char *p;
static char buf[50];
+ static size_t buflen = 0;
STRCPY(buf, ":");
+ buflen = 1;
switch ((int) OP(op))
{
@@ -5491,7 +5500,7 @@ regprop(char_u *op)
case MOPEN + 7:
case MOPEN + 8:
case MOPEN + 9:
- sprintf(buf + STRLEN(buf), "MOPEN%d", OP(op) - MOPEN);
+ buflen += sprintf(buf + buflen, "MOPEN%d", OP(op) - MOPEN);
p = NULL;
break;
case MCLOSE + 0:
@@ -5506,7 +5515,7 @@ regprop(char_u *op)
case MCLOSE + 7:
case MCLOSE + 8:
case MCLOSE + 9:
- sprintf(buf + STRLEN(buf), "MCLOSE%d", OP(op) - MCLOSE);
+ buflen += sprintf(buf + buflen, "MCLOSE%d", OP(op) - MCLOSE);
p = NULL;
break;
case BACKREF + 1:
@@ -5518,7 +5527,7 @@ regprop(char_u *op)
case BACKREF + 7:
case BACKREF + 8:
case BACKREF + 9:
- sprintf(buf + STRLEN(buf), "BACKREF%d", OP(op) - BACKREF);
+ buflen += sprintf(buf + buflen, "BACKREF%d", OP(op) - BACKREF);
p = NULL;
break;
case NOPEN:
@@ -5537,7 +5546,7 @@ regprop(char_u *op)
case ZOPEN + 7:
case ZOPEN + 8:
case ZOPEN + 9:
- sprintf(buf + STRLEN(buf), "ZOPEN%d", OP(op) - ZOPEN);
+ buflen += sprintf(buf + buflen, "ZOPEN%d", OP(op) - ZOPEN);
p = NULL;
break;
case ZCLOSE + 1:
@@ -5549,7 +5558,7 @@ regprop(char_u *op)
case ZCLOSE + 7:
case ZCLOSE + 8:
case ZCLOSE + 9:
- sprintf(buf + STRLEN(buf), "ZCLOSE%d", OP(op) - ZCLOSE);
+ buflen += sprintf(buf + buflen, "ZCLOSE%d", OP(op) - ZCLOSE);
p = NULL;
break;
case ZREF + 1:
@@ -5561,7 +5570,7 @@ regprop(char_u *op)
case ZREF + 7:
case ZREF + 8:
case ZREF + 9:
- sprintf(buf + STRLEN(buf), "ZREF%d", OP(op) - ZREF);
+ buflen += sprintf(buf + buflen, "ZREF%d", OP(op) - ZREF);
p = NULL;
break;
#endif
@@ -5602,7 +5611,7 @@ regprop(char_u *op)
case BRACE_COMPLEX + 7:
case BRACE_COMPLEX + 8:
case BRACE_COMPLEX + 9:
- sprintf(buf + STRLEN(buf), "BRACE_COMPLEX%d", OP(op) - BRACE_COMPLEX);
+ buflen += sprintf(buf + buflen, "BRACE_COMPLEX%d", OP(op) - BRACE_COMPLEX);
p = NULL;
break;
case MULTIBYTECODE:
@@ -5612,12 +5621,12 @@ regprop(char_u *op)
p = "NEWL";
break;
default:
- sprintf(buf + STRLEN(buf), "corrupt %d", OP(op));
+ buflen += sprintf(buf + buflen, "corrupt %d", OP(op));
p = NULL;
break;
}
if (p != NULL)
- STRCAT(buf, p);
+ STRCPY(buf + buflen, p);
return (char_u *)buf;
}
#endif // DEBUG
diff --git a/src/regexp_nfa.c b/src/regexp_nfa.c
index 5e4fadd..4f07a21 100644
--- a/src/regexp_nfa.c
+++ b/src/regexp_nfa.c
@@ -5387,7 +5387,7 @@ recursive_regmatch(
rex.input = rex.line;
}
else
- rex.input = rex.line + STRLEN(rex.line);
+ rex.input = rex.line + reg_getline_len(rex.lnum);
}
if ((int)(rex.input - rex.line) >= state->val)
{
@@ -6937,8 +6937,7 @@ nfa_regmatch(
{
colnr_T pos_col = pos->lnum == rex.lnum + rex.reg_firstlnum
&& pos->col == MAXCOL
- ? (colnr_T)STRLEN(reg_getline(
- pos->lnum - rex.reg_firstlnum))
+ ? reg_getline_len(pos->lnum - rex.reg_firstlnum)
: pos->col;
result = (pos->lnum == rex.lnum + rex.reg_firstlnum
diff --git a/src/scriptfile.c b/src/scriptfile.c
index d5ec7cf..711f576 100644
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -435,8 +435,13 @@ check_script_symlink(int sid)
si = SCRIPT_ITEM(sid);
si->sn_sourced_sid = real_sid;
if (new_sid)
+ {
SCRIPT_ITEM(real_sid)->sn_import_autoload
= si->sn_import_autoload;
+ if (si->sn_autoload_prefix != NULL)
+ SCRIPT_ITEM(real_sid)->sn_autoload_prefix =
+ vim_strsave(si->sn_autoload_prefix);
+ }
}
}
vim_free(real_fname);
@@ -682,7 +687,7 @@ find_script_in_rtp(char_u *name)
{
int sid = -1;
- (void)do_in_path_and_pp(p_rtp, name, DIP_NOAFTER,
+ (void)do_in_path_and_pp(p_rtp, name, DIP_START | DIP_NOAFTER,
find_script_callback, &sid);
return sid;
}
diff --git a/src/search.c b/src/search.c
index 166ef4a..0b39d90 100644
--- a/src/search.c
+++ b/src/search.c
@@ -17,8 +17,8 @@ static void set_vv_searchforward(void);
static int first_submatch(regmmatch_T *rp);
#endif
#ifdef FEAT_FIND_ID
-static void show_pat_in_path(char_u *, int,
- int, int, FILE *, linenr_T *, long);
+static char_u *get_line_and_copy(linenr_T lnum, char_u *buf);
+static void show_pat_in_path(char_u *, int, int, int, FILE *, linenr_T *, long);
#endif
typedef struct searchstat
@@ -32,8 +32,27 @@ typedef struct searchstat
int last_maxcount; // the max count of the last search
} searchstat_T;
-static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, int show_top_bot_msg, char_u *msgbuf, int recompute, int maxcount, long timeout);
+#ifdef FEAT_SEARCH_EXTRA
+static void save_incsearch_state(void);
+static void restore_incsearch_state(void);
+#endif
+static int check_prevcol(char_u *linep, int col, int ch, int *prevcol);
+static int find_rawstring_end(char_u *linep, pos_T *startpos, pos_T *endpos);
+static void find_mps_values(int *initc, int *findc, int *backwards, int switchit);
+static int is_zero_width(char_u *pattern, size_t patternlen, int move, pos_T *cur, int direction);
+static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, int show_top_bot_msg, char_u *msgbuf, size_t msgbuflen, int recompute, int maxcount, long timeout);
static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchstat_T *stat, int recompute, int maxcount, long timeout);
+static int fuzzy_match_compute_score(char_u *str, int strSz, int_u *matches, int numMatches);
+static int fuzzy_match_recursive(char_u *fuzpat, char_u *str, int_u strIdx, int *outScore, char_u *strBegin, int strLen, int_u *srcMatches, int_u *matches, int maxMatches, int nextMatch, int *recursionCount);
+#if defined(FEAT_EVAL) || defined(FEAT_PROTO)
+static int fuzzy_match_item_compare(const void *s1, const void *s2);
+static void fuzzy_match_in_list(list_T *l, char_u *str, int matchseq, char_u *key, callback_T *item_cb, int retmatchpos, list_T *fmatchlist, long max_matches);
+static void do_fuzzymatch(typval_T *argvars, typval_T *rettv, int retmatchpos);
+#endif
+static int fuzzy_match_str_compare(const void *s1, const void *s2);
+static void fuzzy_match_str_sort(fuzmatch_str_T *fm, int sz);
+static int fuzzy_match_func_compare(const void *s1, const void *s2);
+static void fuzzy_match_func_sort(fuzmatch_str_T *fm, int sz);
#define SEARCH_STAT_DEF_TIMEOUT 40L
#define SEARCH_STAT_DEF_MAX_COUNT 99
@@ -69,8 +88,8 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst
*/
static spat_T spats[2] =
{
- {NULL, TRUE, FALSE, {'/', 0, 0, 0L}}, // last used search pat
- {NULL, TRUE, FALSE, {'/', 0, 0, 0L}} // last used substitute pat
+ {NULL, 0, TRUE, FALSE, {'/', 0, 0, 0L}}, // last used search pat
+ {NULL, 0, TRUE, FALSE, {'/', 0, 0, 0L}} // last used substitute pat
};
static int last_idx = 0; // index in spats[] for RE_LAST
@@ -82,8 +101,9 @@ static char_u lastc_bytes[MB_MAXBYTES + 1];
static int lastc_bytelen = 1; // >1 for multi-byte char
// copy of spats[], for keeping the search patterns while executing autocmds
-static spat_T saved_spats[2];
+static spat_T saved_spats[ARRAY_LENGTH(spats)];
static char_u *saved_mr_pattern = NULL;
+static size_t saved_mr_patternlen = 0;
# ifdef FEAT_SEARCH_EXTRA
static int saved_spats_last_idx = 0;
static int saved_spats_no_hlsearch = 0;
@@ -91,6 +111,7 @@ static int saved_spats_no_hlsearch = 0;
// allocated copy of pattern used by search_regcomp()
static char_u *mr_pattern = NULL;
+static size_t mr_patternlen = 0;
#ifdef FEAT_FIND_ID
/*
@@ -123,6 +144,7 @@ typedef struct SearchedFile
int
search_regcomp(
char_u *pat,
+ size_t patlen,
char_u **used_pat,
int pat_save,
int pat_use,
@@ -130,7 +152,6 @@ search_regcomp(
regmmatch_T *regmatch) // return: pattern and ignore-case flag
{
int magic;
- int i;
rc_did_emsg = FALSE;
magic = magic_isset();
@@ -140,6 +161,8 @@ search_regcomp(
*/
if (pat == NULL || *pat == NUL)
{
+ int i;
+
if (pat_use == RE_LAST)
i = last_idx;
else
@@ -154,11 +177,12 @@ search_regcomp(
return FAIL;
}
pat = spats[i].pat;
+ patlen = spats[i].patlen;
magic = spats[i].magic;
no_smartcase = spats[i].no_scs;
}
else if (options & SEARCH_HIS) // put new pattern in history
- add_to_history(HIST_SEARCH, pat, TRUE, NUL);
+ add_to_history(HIST_SEARCH, pat, patlen, TRUE, NUL);
if (used_pat)
*used_pat = pat;
@@ -169,7 +193,11 @@ search_regcomp(
mr_pattern = reverse_text(pat);
else
#endif
- mr_pattern = vim_strsave(pat);
+ mr_pattern = vim_strnsave(pat, patlen);
+ if (mr_pattern == NULL)
+ mr_patternlen = 0;
+ else
+ mr_patternlen = patlen;
/*
* Save the currently used pattern in the appropriate place,
@@ -180,10 +208,10 @@ search_regcomp(
{
// search or global command
if (pat_save == RE_SEARCH || pat_save == RE_BOTH)
- save_re_pat(RE_SEARCH, pat, magic);
+ save_re_pat(RE_SEARCH, pat, patlen, magic);
// substitute or global command
if (pat_save == RE_SUBST || pat_save == RE_BOTH)
- save_re_pat(RE_SUBST, pat, magic);
+ save_re_pat(RE_SUBST, pat, patlen, magic);
}
regmatch->rmm_ic = ignorecase(pat);
@@ -204,13 +232,17 @@ get_search_pat(void)
}
void
-save_re_pat(int idx, char_u *pat, int magic)
+save_re_pat(int idx, char_u *pat, size_t patlen, int magic)
{
if (spats[idx].pat == pat)
return;
vim_free(spats[idx].pat);
- spats[idx].pat = vim_strsave(pat);
+ spats[idx].pat = vim_strnsave(pat, patlen);
+ if (spats[idx].pat == NULL)
+ spats[idx].patlen = 0;
+ else
+ spats[idx].patlen = patlen;
spats[idx].magic = magic;
spats[idx].no_scs = no_smartcase;
last_idx = idx;
@@ -231,19 +263,31 @@ static int save_level = 0;
void
save_search_patterns(void)
{
+ int i;
+
if (save_level++ != 0)
return;
- saved_spats[0] = spats[0];
- if (spats[0].pat != NULL)
- saved_spats[0].pat = vim_strsave(spats[0].pat);
- saved_spats[1] = spats[1];
- if (spats[1].pat != NULL)
- saved_spats[1].pat = vim_strsave(spats[1].pat);
+ for (i = 0; i < (int)ARRAY_LENGTH(spats); ++i)
+ {
+ saved_spats[i] = spats[i];
+ if (spats[i].pat != NULL)
+ {
+ saved_spats[i].pat = vim_strnsave(spats[i].pat, spats[i].patlen);
+ if (saved_spats[i].pat == NULL)
+ saved_spats[i].patlen = 0;
+ else
+ saved_spats[i].patlen = spats[i].patlen;
+ }
+ }
if (mr_pattern == NULL)
saved_mr_pattern = NULL;
else
- saved_mr_pattern = vim_strsave(mr_pattern);
+ saved_mr_pattern = vim_strnsave(mr_pattern, mr_patternlen);
+ if (saved_mr_pattern == NULL)
+ saved_mr_patternlen = 0;
+ else
+ saved_mr_patternlen = mr_patternlen;
#ifdef FEAT_SEARCH_EXTRA
saved_spats_last_idx = last_idx;
saved_spats_no_hlsearch = no_hlsearch;
@@ -253,18 +297,22 @@ save_search_patterns(void)
void
restore_search_patterns(void)
{
+ int i;
+
if (--save_level != 0)
return;
- vim_free(spats[0].pat);
- spats[0] = saved_spats[0];
+ for (i = 0; i < (int)ARRAY_LENGTH(spats); ++i)
+ {
+ vim_free(spats[i].pat);
+ spats[i] = saved_spats[i];
+ }
#if defined(FEAT_EVAL)
set_vv_searchforward();
#endif
- vim_free(spats[1].pat);
- spats[1] = saved_spats[1];
vim_free(mr_pattern);
mr_pattern = saved_mr_pattern;
+ mr_patternlen = saved_mr_patternlen;
#ifdef FEAT_SEARCH_EXTRA
last_idx = saved_spats_last_idx;
set_no_hlsearch(saved_spats_no_hlsearch);
@@ -275,9 +323,15 @@ restore_search_patterns(void)
void
free_search_patterns(void)
{
- vim_free(spats[0].pat);
- vim_free(spats[1].pat);
+ int i;
+
+ for (i = 0; i < (int)ARRAY_LENGTH(spats); ++i)
+ {
+ VIM_CLEAR(spats[i].pat);
+ spats[i].patlen = 0;
+ }
VIM_CLEAR(mr_pattern);
+ mr_patternlen = 0;
}
#endif
@@ -308,7 +362,13 @@ save_last_search_pattern(void)
saved_last_search_spat = spats[RE_SEARCH];
if (spats[RE_SEARCH].pat != NULL)
- saved_last_search_spat.pat = vim_strsave(spats[RE_SEARCH].pat);
+ {
+ saved_last_search_spat.pat = vim_strnsave(spats[RE_SEARCH].pat, spats[RE_SEARCH].patlen);
+ if (saved_last_search_spat.pat == NULL)
+ saved_last_search_spat.patlen = 0;
+ else
+ saved_last_search_spat.patlen = spats[RE_SEARCH].patlen;
+ }
saved_last_idx = last_idx;
saved_no_hlsearch = no_hlsearch;
}
@@ -328,6 +388,7 @@ restore_last_search_pattern(void)
vim_free(spats[RE_SEARCH].pat);
spats[RE_SEARCH] = saved_last_search_spat;
saved_last_search_spat.pat = NULL;
+ saved_last_search_spat.patlen = 0;
# if defined(FEAT_EVAL)
set_vv_searchforward();
# endif
@@ -513,7 +574,12 @@ set_last_search_pat(
if (*s == NUL)
spats[idx].pat = NULL;
else
- spats[idx].pat = vim_strsave(s);
+ {
+ spats[idx].patlen = STRLEN(s);
+ spats[idx].pat = vim_strnsave(s, spats[idx].patlen);
+ }
+ if (spats[idx].pat == NULL)
+ spats[idx].patlen = 0;
spats[idx].magic = magic;
spats[idx].no_scs = FALSE;
spats[idx].off.dir = '/';
@@ -532,7 +598,11 @@ set_last_search_pat(
if (spats[idx].pat == NULL)
saved_spats[idx].pat = NULL;
else
- saved_spats[idx].pat = vim_strsave(spats[idx].pat);
+ saved_spats[idx].pat = vim_strnsave(spats[idx].pat, spats[idx].patlen);
+ if (saved_spats[idx].pat == NULL)
+ saved_spats[idx].patlen = 0;
+ else
+ saved_spats[idx].patlen = spats[idx].patlen;
# ifdef FEAT_SEARCH_EXTRA
saved_spats_last_idx = last_idx;
# endif
@@ -560,7 +630,7 @@ last_pat_prog(regmmatch_T *regmatch)
return;
}
++emsg_off; // So it doesn't beep if bad expr
- (void)search_regcomp((char_u *)"", NULL, 0, last_idx, SEARCH_KEEP, regmatch);
+ (void)search_regcomp((char_u *)"", 0, NULL, 0, last_idx, SEARCH_KEEP, regmatch);
--emsg_off;
}
#endif
@@ -594,6 +664,7 @@ searchit(
pos_T *end_pos, // set to end of the match, unless NULL
int dir,
char_u *pat,
+ size_t patlen,
long count,
int options,
int pat_use, // which pattern to use when "pat" is empty
@@ -623,8 +694,9 @@ searchit(
linenr_T stop_lnum = 0; // stop after this line number when != 0
int unused_timeout_flag = FALSE;
int *timed_out = &unused_timeout_flag; // set when timed out.
+ int search_from_match_end; // vi-compatible search?
- if (search_regcomp(pat, NULL, RE_SEARCH, pat_use,
+ if (search_regcomp(pat, patlen, NULL, RE_SEARCH, pat_use,
(options & (SEARCH_HIS + SEARCH_KEEP)), &regmatch) == FAIL)
{
if ((options & SEARCH_MSG) && !rc_did_emsg)
@@ -632,6 +704,8 @@ searchit(
return FAIL;
}
+ search_from_match_end = vim_strchr(p_cpo, CPO_SEARCH) != NULL;
+
if (extra_arg != NULL)
{
stop_lnum = extra_arg->sa_stop_lnum;
@@ -778,7 +852,7 @@ searchit(
* of the match, otherwise continue one position
* forward.
*/
- if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
+ if (search_from_match_end)
{
if (nmatched > 1)
{
@@ -890,7 +964,7 @@ searchit(
* of the match, otherwise continue one position
* forward.
*/
- if (vim_strchr(p_cpo, CPO_SEARCH) != NULL)
+ if (search_from_match_end)
{
if (nmatched > 1)
break;
@@ -1173,12 +1247,14 @@ do_search(
int search_delim, // the delimiter for the search, e.g. '%' in
// s%regex%replacement%
char_u *pat,
+ size_t patlen,
long count,
int options,
searchit_arg_T *sia) // optional arguments or NULL
{
pos_T pos; // position of the last match
char_u *searchstr;
+ size_t searchstrlen;
soffset_T old_off;
int retval; // Return value
char_u *p;
@@ -1186,10 +1262,13 @@ do_search(
char_u *dircp;
char_u *strcopy = NULL;
char_u *ps;
+ int show_search_stats;
char_u *msgbuf = NULL;
- size_t len;
+ size_t msgbuflen = 0;
int has_offset = FALSE;
+ searchcmdlen = 0;
+
/*
* A line offset is not remembered, this is vi compatible.
*/
@@ -1267,24 +1346,28 @@ do_search(
int show_top_bot_msg = FALSE;
searchstr = pat;
+ searchstrlen = patlen;
+
dircp = NULL;
// use previous pattern
if (pat == NULL || *pat == NUL || *pat == search_delim)
{
if (spats[RE_SEARCH].pat == NULL) // no previous pattern
{
- searchstr = spats[RE_SUBST].pat;
- if (searchstr == NULL)
+ if (spats[RE_SUBST].pat == NULL)
{
emsg(_(e_no_previous_regular_expression));
retval = 0;
goto end_do_search;
}
+ searchstr = spats[RE_SUBST].pat;
+ searchstrlen = spats[RE_SUBST].patlen;
}
else
{
// make search_regcomp() use spats[RE_SEARCH].pat
searchstr = (char_u *)"";
+ searchstrlen = 0;
}
}
@@ -1299,13 +1382,17 @@ do_search(
&strcopy, NULL, NULL);
if (strcopy != ps)
{
+ size_t len = STRLEN(strcopy);
// made a copy of "pat" to change "\?" to "?"
- searchcmdlen += (int)(STRLEN(pat) - STRLEN(strcopy));
+ searchcmdlen += (int)(patlen - len);
pat = strcopy;
+ patlen = len;
searchstr = strcopy;
+ searchstrlen = len;
}
if (*p == search_delim)
{
+ searchstrlen = p - pat;
dircp = p; // remember where we put the NUL
*p++ = NUL;
}
@@ -1344,16 +1431,19 @@ do_search(
// compute length of search command for get_address()
searchcmdlen += (int)(p - pat);
+ patlen -= p - pat;
pat = p; // put pat after search command
}
+ show_search_stats = FALSE;
if ((options & SEARCH_ECHO) && messaging()
&& !msg_silent
&& (!cmd_silent || !shortmess(SHM_SEARCHCOUNT)))
{
- char_u *trunc;
char_u off_buf[40];
size_t off_len = 0;
+ size_t plen;
+ size_t msgbufsize;
// Compute msg_row early.
msg_start();
@@ -1362,24 +1452,28 @@ do_search(
if (!cmd_silent &&
(spats[0].off.line || spats[0].off.end || spats[0].off.off))
{
- p = off_buf;
- *p++ = dirc;
+ off_buf[off_len++] = dirc;
if (spats[0].off.end)
- *p++ = 'e';
+ off_buf[off_len++] = 'e';
else if (!spats[0].off.line)
- *p++ = 's';
+ off_buf[off_len++] = 's';
if (spats[0].off.off > 0 || spats[0].off.line)
- *p++ = '+';
- *p = NUL;
+ off_buf[off_len++] = '+';
+ off_buf[off_len] = NUL;
if (spats[0].off.off != 0 || spats[0].off.line)
- sprintf((char *)p, "%ld", spats[0].off.off);
- off_len = STRLEN(off_buf);
+ off_len += vim_snprintf((char *)off_buf + off_len, sizeof(off_buf) - off_len, "%ld", spats[0].off.off);
}
if (*searchstr == NUL)
+ {
p = spats[0].pat;
+ plen = spats[0].patlen;
+ }
else
+ {
p = searchstr;
+ plen = searchstrlen;
+ }
if (!shortmess(SHM_SEARCHCOUNT) || cmd_silent)
{
@@ -1389,45 +1483,53 @@ do_search(
// msg_strtrunc() will shorten in the middle.
if (msg_scrolled != 0 && !cmd_silent)
// Use all the columns.
- len = (int)(Rows - msg_row) * Columns - 1;
+ msgbufsize = (int)(Rows - msg_row) * Columns - 1;
else
// Use up to 'showcmd' column.
- len = (int)(Rows - msg_row - 1) * Columns + sc_col - 1;
- if (len < STRLEN(p) + off_len + SEARCH_STAT_BUF_LEN + 3)
- len = STRLEN(p) + off_len + SEARCH_STAT_BUF_LEN + 3;
+ msgbufsize = (int)(Rows - msg_row - 1) * Columns + sc_col - 1;
+ if (msgbufsize < plen + off_len + SEARCH_STAT_BUF_LEN + 3)
+ msgbufsize = plen + off_len + SEARCH_STAT_BUF_LEN + 3;
}
else
// Reserve enough space for the search pattern + offset.
- len = STRLEN(p) + off_len + 3;
+ msgbufsize = plen + off_len + 3;
vim_free(msgbuf);
- msgbuf = alloc(len);
- if (msgbuf != NULL)
+ msgbuf = alloc(msgbufsize);
+ if (msgbuf == NULL)
{
- vim_memset(msgbuf, ' ', len);
- msgbuf[len - 1] = NUL;
+ msgbuflen = 0;
+ }
+ else
+ {
+ vim_memset(msgbuf, ' ', msgbufsize);
+ msgbuflen = msgbufsize - 1;
+ msgbuf[msgbuflen] = NUL;
// do not fill the msgbuf buffer, if cmd_silent is set, leave it
// empty for the search_stat feature.
if (!cmd_silent)
{
+ char_u *trunc;
+
msgbuf[0] = dirc;
if (enc_utf8 && utf_iscomposing(utf_ptr2char(p)))
{
// Use a space to draw the composing char on.
msgbuf[1] = ' ';
- mch_memmove(msgbuf + 2, p, STRLEN(p));
+ mch_memmove(msgbuf + 2, p, plen);
}
else
- mch_memmove(msgbuf + 1, p, STRLEN(p));
+ mch_memmove(msgbuf + 1, p, plen);
if (off_len > 0)
- mch_memmove(msgbuf + STRLEN(p) + 1, off_buf, off_len);
+ mch_memmove(msgbuf + plen + 1, off_buf, off_len);
trunc = msg_strtrunc(msgbuf, TRUE);
if (trunc != NULL)
{
vim_free(msgbuf);
msgbuf = trunc;
+ msgbuflen = STRLEN(msgbuf);
}
#ifdef FEAT_RIGHTLEFT
@@ -1448,7 +1550,7 @@ do_search(
// move reversed text to beginning of buffer
while (*r != NUL && *r == ' ')
r++;
- pat_len = msgbuf + STRLEN(msgbuf) - r;
+ pat_len = msgbuf + msgbuflen - r;
mch_memmove(msgbuf, r, pat_len);
// overwrite old text
if ((size_t)(r - msgbuf) >= pat_len)
@@ -1466,7 +1568,10 @@ do_search(
out_flush();
msg_nowait = TRUE; // don't wait for this message
}
- }
+
+ if (!shortmess(SHM_SEARCHCOUNT))
+ show_search_stats = TRUE;
+ } // msgbuf != NULL
}
/*
@@ -1507,7 +1612,7 @@ do_search(
*/
c = searchit(curwin, curbuf, &pos, NULL,
dirc == '/' ? FORWARD : BACKWARD,
- searchstr, count, spats[0].off.end + (options &
+ searchstr, searchstrlen, count, spats[0].off.end + (options &
(SEARCH_KEEP + SEARCH_PEEK + SEARCH_HIS
+ SEARCH_MSG + SEARCH_START
+ ((pat != NULL && *pat == ';') ? 0 : SEARCH_NOOF))),
@@ -1574,14 +1679,9 @@ do_search(
}
// Show [1/15] if 'S' is not in 'shortmess'.
- if ((options & SEARCH_ECHO)
- && messaging()
- && !msg_silent
- && c != FAIL
- && !shortmess(SHM_SEARCHCOUNT)
- && msgbuf != NULL)
+ if (show_search_stats)
cmdline_search_stat(dirc, &pos, &curwin->w_cursor,
- show_top_bot_msg, msgbuf,
+ show_top_bot_msg, msgbuf, msgbuflen,
(count != 1 || has_offset
#ifdef FEAT_FOLDING
|| (!(fdo_flags & FDO_SEARCH)
@@ -1612,6 +1712,7 @@ do_search(
goto end_do_search;
}
++pat;
+ --patlen;
}
if (options & SEARCH_MARK)
@@ -2825,7 +2926,7 @@ showmatch(
* Returns TRUE, FALSE or -1 for failure.
*/
static int
-is_zero_width(char_u *pattern, int move, pos_T *cur, int direction)
+is_zero_width(char_u *pattern, size_t patternlen, int move, pos_T *cur, int direction)
{
regmmatch_T regmatch;
int nmatched = 0;
@@ -2835,9 +2936,12 @@ is_zero_width(char_u *pattern, int move, pos_T *cur, int direction)
int flag = 0;
if (pattern == NULL)
+ {
pattern = spats[last_idx].pat;
+ patternlen = spats[last_idx].patlen;
+ }
- if (search_regcomp(pattern, NULL, RE_SEARCH, RE_SEARCH,
+ if (search_regcomp(pattern, patternlen, NULL, RE_SEARCH, RE_SEARCH,
SEARCH_KEEP, &regmatch) == FAIL)
return -1;
@@ -2855,7 +2959,7 @@ is_zero_width(char_u *pattern, int move, pos_T *cur, int direction)
flag = SEARCH_START;
}
- if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, 1,
+ if (searchit(curwin, curbuf, &pos, NULL, direction, pattern, patternlen, 1,
SEARCH_KEEP + flag, RE_SEARCH, NULL) != FAIL)
{
// Zero-width pattern should match somewhere, then we can check if
@@ -2925,8 +3029,8 @@ current_search(
}
// Is the pattern is zero-width?, this time, don't care about the direction
- zero_width = is_zero_width(spats[last_idx].pat, TRUE, &curwin->w_cursor,
- FORWARD);
+ zero_width = is_zero_width(spats[last_idx].pat, spats[last_idx].patlen,
+ TRUE, &curwin->w_cursor, FORWARD);
if (zero_width == -1)
return FAIL; // pattern not found
@@ -2957,7 +3061,7 @@ current_search(
result = searchit(curwin, curbuf, &pos, &end_pos,
(dir ? FORWARD : BACKWARD),
- spats[last_idx].pat, (long) (i ? count : 1),
+ spats[last_idx].pat, spats[last_idx].patlen, (long) (i ? count : 1),
SEARCH_KEEP | flags, RE_SEARCH, NULL);
p_ws = old_p_ws;
@@ -3061,6 +3165,7 @@ cmdline_search_stat(
pos_T *cursor_pos,
int show_top_bot_msg,
char_u *msgbuf,
+ size_t msgbuflen,
int recompute,
int maxcount,
long timeout)
@@ -3079,34 +3184,33 @@ cmdline_search_stat(
if (curwin->w_p_rl && *curwin->w_p_rlc == 's')
{
if (stat.incomplete == 1)
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]");
+ len = vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]");
else if (stat.cnt > maxcount && stat.cur > maxcount)
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]",
+ len = vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]",
maxcount, maxcount);
else if (stat.cnt > maxcount)
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]",
+ len = vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/%d]",
maxcount, stat.cur);
else
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]",
+ len = vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]",
stat.cnt, stat.cur);
}
else
#endif
{
if (stat.incomplete == 1)
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]");
+ len = vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[?/??]");
else if (stat.cnt > maxcount && stat.cur > maxcount)
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]",
+ len = vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[>%d/>%d]",
maxcount, maxcount);
else if (stat.cnt > maxcount)
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>%d]",
+ len = vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/>%d]",
stat.cur, maxcount);
else
- vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]",
+ len = vim_snprintf(t, SEARCH_STAT_BUF_LEN, "[%d/%d]",
stat.cur, stat.cnt);
}
- len = STRLEN(t);
if (show_top_bot_msg && len + 2 < SEARCH_STAT_BUF_LEN)
{
mch_memmove(t + 2, t, len);
@@ -3115,10 +3219,9 @@ cmdline_search_stat(
len += 2;
}
- size_t msgbuf_len = STRLEN(msgbuf);
- if (len > msgbuf_len)
- len = msgbuf_len;
- mch_memmove(msgbuf + msgbuf_len - len, t, len);
+ if (len > msgbuflen)
+ len = msgbuflen;
+ mch_memmove(msgbuf + msgbuflen - len, t, len);
if (dirc == '?' && stat.cur == maxcount + 1)
stat.cur = -1;
@@ -3214,7 +3317,7 @@ update_search_stat(
profile_setlimit(timeout, &start);
#endif
while (!got_int && searchit(curwin, curbuf, &lastpos, &endpos,
- FORWARD, NULL, 1, SEARCH_KEEP, RE_LAST, NULL) != FAIL)
+ FORWARD, NULL, 0, 1, SEARCH_KEEP, RE_LAST, NULL) != FAIL)
{
done_search = TRUE;
#ifdef FEAT_RELTIME
@@ -3342,7 +3445,7 @@ find_pattern_in_path(
pat = alloc(len + 5);
if (pat == NULL)
goto fpip_end;
- sprintf((char *)pat, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
+ vim_snprintf((char *)pat, len + 5, whole ? "\\<%.*s\\>" : "%.*s", len, ptr);
// ignore case according to p_ic, p_scs and pat
regmatch.rm_ic = ignorecase(pat);
regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0);
@@ -3361,8 +3464,7 @@ find_pattern_in_path(
}
if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL))
{
- def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL
- ? p_def : curbuf->b_p_def,
+ def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL ? p_def : curbuf->b_p_def,
magic_isset() ? RE_MAGIC : 0);
if (def_regmatch.regprog == NULL)
goto fpip_end;
@@ -3879,7 +3981,7 @@ exit_matched:
&& action == ACTION_EXPAND
&& !compl_status_sol()
&& *startp != NUL
- && *(p = startp + mb_ptr2len(startp)) != NUL)
+ && *(startp + mb_ptr2len(startp)) != NUL)
goto search_line;
}
line_breakcheck();
@@ -3976,6 +4078,7 @@ show_pat_in_path(
long count)
{
char_u *p;
+ size_t linelen;
if (did_show)
msg_putchar('\n'); // cursor below last one
@@ -3983,9 +4086,10 @@ show_pat_in_path(
gotocmdline(TRUE); // cursor at status line
if (got_int) // 'q' typed at "--more--" message
return;
+ linelen = STRLEN(line);
for (;;)
{
- p = line + STRLEN(line) - 1;
+ p = line + linelen - 1;
if (fp != NULL)
{
// We used fgets(), so get rid of newline at end
@@ -4015,6 +4119,7 @@ show_pat_in_path(
{
if (vim_fgets(line, LSIZE, fp)) // end of file
break;
+ linelen = STRLEN(line);
++*lnum;
}
else
@@ -4022,6 +4127,7 @@ show_pat_in_path(
if (++*lnum > curbuf->b_ml.ml_line_count)
break;
line = ml_get(*lnum);
+ linelen = ml_get_len(*lnum);
}
msg_putchar('\n');
}
@@ -4149,7 +4255,10 @@ f_searchcount(typval_T *argvars, typval_T *rettv)
if (*pattern == NUL)
goto the_end;
vim_free(spats[last_idx].pat);
- spats[last_idx].pat = vim_strsave(pattern);
+ spats[last_idx].patlen = STRLEN(pattern);
+ spats[last_idx].pat = vim_strnsave(pattern, spats[last_idx].patlen);
+ if (spats[last_idx].pat == NULL)
+ spats[last_idx].patlen = 0;
}
if (spats[last_idx].pat == NULL || *spats[last_idx].pat == NUL)
goto the_end; // the previous pattern was never defined
@@ -4983,6 +5092,54 @@ fuzzy_match_str(char_u *str, char_u *pat)
}
/*
+ * Fuzzy match the position of string 'pat' in string 'str'.
+ * Returns a dynamic array of matching positions. If there is no match,
+ * returns NULL.
+ */
+ garray_T *
+fuzzy_match_str_with_pos(char_u *str UNUSED, char_u *pat UNUSED)
+{
+#ifdef FEAT_SEARCH_EXTRA
+ int score = 0;
+ garray_T *match_positions = NULL;
+ int_u matches[MAX_FUZZY_MATCHES];
+ int j = 0;
+
+ if (str == NULL || pat == NULL)
+ return NULL;
+
+ match_positions = ALLOC_ONE(garray_T);
+ if (match_positions == NULL)
+ return NULL;
+ ga_init2(match_positions, sizeof(int_u), 10);
+
+ if (!fuzzy_match(str, pat, FALSE, &score, matches, MAX_FUZZY_MATCHES)
+ || score == 0)
+ {
+ ga_clear(match_positions);
+ vim_free(match_positions);
+ return NULL;
+ }
+
+ for (char_u *p = pat; *p != NUL; MB_PTR_ADV(p))
+ {
+ if (!VIM_ISWHITE(PTR2CHAR(p)))
+ {
+ ga_grow(match_positions, 1);
+ ((int_u *)match_positions->ga_data)[match_positions->ga_len] =
+ matches[j];
+ match_positions->ga_len++;
+ j++;
+ }
+ }
+
+ return match_positions;
+#else
+ return NULL;
+#endif
+}
+
+/*
* Free an array of fuzzy string matches "fuzmatch[count]".
*/
void
diff --git a/src/spell.c b/src/spell.c
index 43c521d..909d426 100644
--- a/src/spell.c
+++ b/src/spell.c
@@ -1336,7 +1336,7 @@ no_spell_checking(win_T *wp)
spell_move_to(
win_T *wp,
int dir, // FORWARD or BACKWARD
- int allwords, // TRUE for "[s"/"]s", FALSE for "[S"/"]S"
+ smt_T behaviour, // Behaviour of the function
int curline,
hlf_T *attrp) // return: attributes of bad word or NULL
// (only when "dir" is FORWARD)
@@ -1441,7 +1441,9 @@ spell_move_to(
if (attr != HLF_COUNT)
{
// We found a bad word. Check the attribute.
- if (allwords || attr == HLF_SPB)
+ if (behaviour == SMT_ALL
+ || (behaviour == SMT_BAD && attr == HLF_SPB)
+ || (behaviour == SMT_RARE && attr == HLF_SPR))
{
// When searching forward only accept a bad word after
// the cursor.
@@ -2953,6 +2955,7 @@ ex_spellrepall(exarg_T *eap UNUSED)
{
pos_T pos = curwin->w_cursor;
char_u *frompat;
+ size_t frompatlen;
char_u *line;
char_u *p;
int save_ws = p_ws;
@@ -2970,7 +2973,7 @@ ex_spellrepall(exarg_T *eap UNUSED)
frompat = alloc(repl_from_len + 7);
if (frompat == NULL)
return;
- sprintf((char *)frompat, "\\V\\<%s\\>", repl_from);
+ frompatlen = vim_snprintf((char *)frompat, repl_from_len + 7, "\\V\\<%s\\>", repl_from);
p_ws = FALSE;
sub_nsubs = 0;
@@ -2978,7 +2981,7 @@ ex_spellrepall(exarg_T *eap UNUSED)
curwin->w_cursor.lnum = 0;
while (!got_int)
{
- if (do_search(NULL, '/', '/', frompat, 1L, SEARCH_KEEP, NULL) == 0
+ if (do_search(NULL, '/', '/', frompat, frompatlen, 1L, SEARCH_KEEP, NULL) == 0
|| u_save_cursor() == FAIL)
break;
diff --git a/src/spellsuggest.c b/src/spellsuggest.c
index c6e6183..b305bfb 100644
--- a/src/spellsuggest.c
+++ b/src/spellsuggest.c
@@ -512,7 +512,7 @@ spell_suggest(int count)
badlen = ml_get_curline_len() - (int)curwin->w_cursor.col;
}
// Find the start of the badly spelled word.
- else if (spell_move_to(curwin, FORWARD, TRUE, TRUE, NULL) == 0
+ else if (spell_move_to(curwin, FORWARD, SMT_ALL, TRUE, NULL) == 0
|| curwin->w_cursor.col > prev_cursor.col)
{
// No bad word or it starts after the cursor: use the word under the
diff --git a/src/strings.c b/src/strings.c
index 33de175..6b2ff0a 100644
--- a/src/strings.c
+++ b/src/strings.c
@@ -151,7 +151,7 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
char_u *p;
char_u *d;
char_u *escaped_string;
- int l;
+ size_t l;
int csh_like;
int fish_like;
char_u *shname;
@@ -272,8 +272,9 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline)
if (do_special && find_cmdline_var(p, &l) >= 0)
{
*d++ = '\\'; // insert backslash
- while (--l >= 0) // copy the var
- *d++ = *p++;
+ memcpy(d, p, l); // copy the var
+ d += l;
+ p += l;
continue;
}
if (*p == '\\' && fish_like)
diff --git a/src/structs.h b/src/structs.h
index 36339c4..7e21f0f 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -3222,6 +3222,8 @@ struct file_buffer
#ifdef FEAT_FOLDING
char_u *b_p_cms; // 'commentstring'
#endif
+ char_u *b_p_cot; // 'completeopt' local value
+ unsigned b_cot_flags; // flags for 'completeopt'
char_u *b_p_cpt; // 'complete'
#ifdef BACKSLASH_IN_FILENAME
char_u *b_p_csl; // 'completeslash'
@@ -4468,6 +4470,8 @@ typedef struct
char_u *pum_kind; // extra kind text (may be truncated)
char_u *pum_extra; // extra menu text (may be truncated)
char_u *pum_info; // extra info
+ int pum_score; // fuzzy match score
+ int pum_idx; // index of item before sorting by score
} pumitem_T;
/*
@@ -4795,6 +4799,7 @@ typedef struct soffset
typedef struct spat
{
char_u *pat; // the pattern (in allocated memory) or NULL
+ size_t patlen; // the length of the pattern (0 if pat is NULL)
int magic; // magicness of the pattern
int no_scs; // no smartcase for this pattern
soffset_T off;
diff --git a/src/tag.c b/src/tag.c
index 9117d0f..d406fde 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -3901,6 +3901,8 @@ jumpto_tag(
str = skip_regexp(pbuf + 1, pbuf[0], FALSE) + 1;
if (str > pbuf_end - 1) // search command with nothing following
{
+ size_t pbuflen = pbuf_end - pbuf;
+
save_p_ws = p_ws;
save_p_ic = p_ic;
save_p_scs = p_scs;
@@ -3914,7 +3916,7 @@ jumpto_tag(
else
// start search before first line
curwin->w_cursor.lnum = 0;
- if (do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, (long)1,
+ if (do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, pbuflen - 1, (long)1,
search_options, NULL))
retval = OK;
else
@@ -3926,7 +3928,7 @@ jumpto_tag(
* try again, ignore case now
*/
p_ic = TRUE;
- if (!do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, (long)1,
+ if (!do_search(NULL, pbuf[0], pbuf[0], pbuf + 1, pbuflen - 1, (long)1,
search_options, NULL))
{
/*
@@ -3936,14 +3938,14 @@ jumpto_tag(
(void)test_for_static(&tagp);
cc = *tagp.tagname_end;
*tagp.tagname_end = NUL;
- sprintf((char *)pbuf, "^%s\\s\\*(", tagp.tagname);
- if (!do_search(NULL, '/', '/', pbuf, (long)1,
+ pbuflen = vim_snprintf((char *)pbuf, LSIZE, "^%s\\s\\*(", tagp.tagname);
+ if (!do_search(NULL, '/', '/', pbuf, pbuflen, (long)1,
search_options, NULL))
{
// Guess again: "^char * \<func ("
- sprintf((char *)pbuf, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
+ pbuflen = vim_snprintf((char *)pbuf, LSIZE, "^\\[#a-zA-Z_]\\.\\*\\<%s\\s\\*(",
tagp.tagname);
- if (!do_search(NULL, '/', '/', pbuf, (long)1,
+ if (!do_search(NULL, '/', '/', pbuf, pbuflen, (long)1,
search_options, NULL))
found = 0;
}
diff --git a/src/terminal.c b/src/terminal.c
index 25a6a5d..648fc78 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -538,9 +538,16 @@ term_start(
split_ea.addr_count = 1;
}
+ int cmod_split_modified = FALSE;
if (vertical)
+ {
+ if (!(cmdmod.cmod_split & WSP_VERT))
+ cmod_split_modified = TRUE;
cmdmod.cmod_split |= WSP_VERT;
+ }
ex_splitview(&split_ea);
+ if (cmod_split_modified)
+ cmdmod.cmod_split &= ~WSP_VERT;
if (curwin == old_curwin)
{
// split failed
@@ -6164,8 +6171,16 @@ f_term_getjob(typval_T *argvars, typval_T *rettv)
buf = term_get_buf(argvars, "term_getjob()");
if (buf == NULL)
{
- rettv->v_type = VAR_SPECIAL;
- rettv->vval.v_number = VVAL_NULL;
+ if (in_vim9script())
+ {
+ rettv->v_type = VAR_JOB;
+ rettv->vval.v_job = NULL;
+ }
+ else
+ {
+ rettv->v_type = VAR_SPECIAL;
+ rettv->vval.v_number = VVAL_NULL;
+ }
return;
}
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index b41f5f4..e31d2b5 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -143,6 +143,7 @@ NEW_TESTS = \
test_file_perm \
test_file_size \
test_filechanged \
+ test_filecopy \
test_fileformat \
test_filetype \
test_filter_cmd \
@@ -278,6 +279,7 @@ NEW_TESTS = \
test_spell \
test_spell_utf8 \
test_spellfile \
+ test_spellrare \
test_startup \
test_startup_utf8 \
test_stat \
@@ -403,6 +405,7 @@ NEW_TESTS_RES = \
test_expr.res \
test_file_size.res \
test_filechanged.res \
+ test_filecopy.res \
test_fileformat.res \
test_filetype.res \
test_filter_cmd.res \
@@ -521,6 +524,7 @@ NEW_TESTS_RES = \
test_spell.res \
test_spell_utf8.res \
test_spellfile.res \
+ test_spellrare.res \
test_startup.res \
test_stat.res \
test_statusline.res \
diff --git a/src/testdir/dumps/Test_balloon_eval_term_03.dump b/src/testdir/dumps/Test_balloon_eval_term_03.dump
new file mode 100644
index 0000000..ccbe9d0
--- /dev/null
+++ b/src/testdir/dumps/Test_balloon_eval_term_03.dump
@@ -0,0 +1,10 @@
+| +0&#ffffff0@38|e|n|o| |e|n|o| |e|n>o
+| @38|o|w|t| |o|X|t| |o|w|t
+| @27| +0#0000001#ffd7ff255@17|e+0#0000000#ffffff0|r|h|t
+| +0#4040ff13&@27| +0#0000001#ffd7ff255|:|6| |n|m|u|l|o|c| |2| |e|n|i|l| | +0#4040ff13#ffffff0@2|~
+| @27| +0#0000001#ffd7ff255@12|<|o|X|t| | +0#4040ff13#ffffff0@2|~
+| @27| +0#0000001#ffd7ff255@17| +0#4040ff13#ffffff0@2|~
+| @48|~
+| @48|~
+| @48|~
+|:+0#0000000&|c|a|l@1| |T|r|i|g@1|e|r|(|)| @16|1|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_mouse_popup_position_01.dump b/src/testdir/dumps/Test_mouse_popup_position_01.dump
new file mode 100644
index 0000000..54ba886
--- /dev/null
+++ b/src/testdir/dumps/Test_mouse_popup_position_01.dump
@@ -0,0 +1,20 @@
+|0+0&#ffffff0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |1|0| |1@1| |1|2| |1|3| |1|4| |1|5| |1|6| |1|7| >1|8| |1|9|
+|~+0#4040ff13&| @31| +0#0000001#ffd7ff255|U|n|d|o| @11
+|~+0#4040ff13#ffffff0| @31| +0#0000001#ffd7ff255@16
+|~+0#4040ff13#ffffff0| @31| +0#0000001#ffd7ff255|P|a|s|t|e| @10
+|~+0#4040ff13#ffffff0| @31| +0#0000001#ffd7ff255@16
+|~+0#4040ff13#ffffff0| @31| +0#0000001#ffd7ff255|S|e|l|e|c|t| |W|o|r|d| @4
+|~+0#4040ff13#ffffff0| @31| +0#0000001#ffd7ff255|S|e|l|e|c|t| |S|e|n|t|e|n|c|e|
+|~+0#4040ff13#ffffff0| @31| +0#0000001#ffd7ff255|S|e|l|e|c|t| |P|a|r|a|g|r|a|p|h
+|~+0#4040ff13#ffffff0| @31| +0#0000001#ffd7ff255|S|e|l|e|c|t| |L|i|n|e| @4
+|~+0#4040ff13#ffffff0| @31| +0#0000001#ffd7ff255|S|e|l|e|c|t| |B|l|o|c|k| @3
+|~+0#4040ff13#ffffff0| @31| +0#0000001#ffd7ff255|S|e|l|e|c|t| |A|l@1| @5
+|~+0#4040ff13#ffffff0| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|:+0#0000000&|c|a|l@1| |T|r|i|g@1|e|r|(|4|5|)| @14|1|,|1| @10|A|l@1|
diff --git a/src/testdir/dumps/Test_mouse_popup_position_02.dump b/src/testdir/dumps/Test_mouse_popup_position_02.dump
new file mode 100644
index 0000000..7dfab52
--- /dev/null
+++ b/src/testdir/dumps/Test_mouse_popup_position_02.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0|9|1| |8>1| |7|1| |6|1| |5|1| |4|1| |3|1| |2|1| |1@1| |0|1| |9| |8| |7| |6| |5| |4| |3| |2| |1| |0
+| +0#0000001#ffd7ff255@11|o|d|n|U| | +0#4040ff13#ffffff0@31|~
+| +0#0000001#ffd7ff255@16| +0#4040ff13#ffffff0@31|~
+| +0#0000001#ffd7ff255@10|e|t|s|a|P| | +0#4040ff13#ffffff0@31|~
+| +0#0000001#ffd7ff255@16| +0#4040ff13#ffffff0@31|~
+| +0#0000001#ffd7ff255@4|d|r|o|W| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@31|~
+| +0#0000001#ffd7ff255|e|c|n|e|t|n|e|S| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@31|~
+|h+0#0000001#ffd7ff255|p|a|r|g|a|r|a|P| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@31|~
+| +0#0000001#ffd7ff255@4|e|n|i|L| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@31|~
+| +0#0000001#ffd7ff255@3|k|c|o|l|B| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@31|~
+| +0#0000001#ffd7ff255@5|l@1|A| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@31|~
+| @48|~
+| @48|~
+| @48|~
+| @48|~
+| @48|~
+| @48|~
+| @48|~
+| @48|~
+|:+0#0000000&|c|a|l@1| |T|r|i|g@1|e|r|(|5|0| |+| |1| |-| |4|5|)| @5|1|,|4|5| @9|A|l@1|
diff --git a/src/testdir/dumps/Test_popup_command_rl.dump b/src/testdir/dumps/Test_popup_command_rl.dump
new file mode 100644
index 0000000..b38ccd9
--- /dev/null
+++ b/src/testdir/dumps/Test_popup_command_rl.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@51|e|v|i|f| |r|u|o|f| |e@1|r|h|t| |o|w|t| |e|n|o
+| @46|e|v|i|f| |r|u|o|f| |e@1|r|h|t>X| |o|w|t| |e|n|o| |d|n|a
+| @45| +0#0000001#ffd7ff255@12|o|d|n|U| |w+0#0000000#ffffff0|t| |e|r|o|m| |e|n|o
+| +0#4040ff13&@45| +0#0000001#ffd7ff255@17| +0#4040ff13#ffffff0@9|~
+| @45| +0#0000001#ffd7ff255@11|e|t|s|a|P| | +0#4040ff13#ffffff0@9|~
+| @45| +0#0000001#ffd7ff255@17| +0#4040ff13#ffffff0@9|~
+| @45| +0#0000001#ffd7ff255@5|d|r|o|W| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@9|~
+| @45| +0#0000001#ffd7ff255@1|e|c|n|e|t|n|e|S| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@9|~
+| @45| +0#0000001#ffd7ff255|h|p|a|r|g|a|r|a|P| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@9|~
+| @45| +0#0000001#ffd7ff255@5|e|n|i|L| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@9|~
+| @45| +0#0000001#ffd7ff255@4|k|c|o|l|B| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@9|~
+| @45| +0#0000001#ffd7ff255@6|l@1|A| |t|c|e|l|e|S| | +0#4040ff13#ffffff0@9|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+|:+0#0000000&|p|o|p|u|p| |P|o|p|U|p| @62
diff --git a/src/testdir/dumps/Test_pum_highlights_03.dump b/src/testdir/dumps/Test_pum_highlights_03.dump
new file mode 100644
index 0000000..77d87d4
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_03.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o> @72
+|f+0#00e0e07#e0e0e08|o|o+0#0000001&| @4|f|o@1|k|i|n|d| | +0#4040ff13#ffffff0@58
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|f|o@1| @1|f|o@1|k|i|n|d| | +0#4040ff13#ffffff0@58
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|b|a|r| @1|f|o@1|k|i|n|d| | +0#4040ff13#ffffff0@58
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|B|a|z| @1|f|o@1|k|i|n|d| | +0#4040ff13#ffffff0@58
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|b|a|l|a| |f|o@1|k|i|n|d| | +0#4040ff13#ffffff0@58
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |9| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_highlights_04.dump b/src/testdir/dumps/Test_pum_highlights_04.dump
new file mode 100644
index 0000000..486a59e
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_04.dump
@@ -0,0 +1,20 @@
+|你*0&#ffffff0> +&@72
+|你*0#00e0e07#e0e0e08|好*0#0000001&| +&@10| +0#4040ff13#ffffff0@59
+|你*0#0000e05#ffd7ff255|好*0#0000001&|吗| +&@8| +0#4040ff13#ffffff0@59
+|你*0#0000e05#ffd7ff255|不*0#0000001&|好|吗| +&@6| +0#4040ff13#ffffff0@59
+|你*0#0000e05#ffd7ff255|可*0#0000001&|好|吗| +&@6| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |9| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_highlights_05.dump b/src/testdir/dumps/Test_pum_highlights_05.dump
new file mode 100644
index 0000000..a2ce2cf
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_05.dump
@@ -0,0 +1,20 @@
+|你*0&#ffffff0|吗> +&@70
+|你*0#00e0e07#e0e0e08|好*0#0000001&|吗*0#00e0e07&| +0#0000001&@8| +0#4040ff13#ffffff0@59
+|你*0#0000e05#ffd7ff255|不*0#0000001&|好|吗*0#0000e05&| +0#0000001&@6| +0#4040ff13#ffffff0@59
+|你*0#0000e05#ffd7ff255|可*0#0000001&|好|吗*0#0000e05&| +0#0000001&@6| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |9| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_highlights_06.dump b/src/testdir/dumps/Test_pum_highlights_06.dump
new file mode 100644
index 0000000..3eb1524
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_06.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@71> |o|f
+| +0#4040ff13&@58| +0#0000001#e0e0e08|d|n|i|k|o@1|f| @4|o|o+0#00e0e07&|f
+| +0#4040ff13#ffffff0@58| +0#0000001#ffd7ff255|d|n|i|k|o@1|f| @1|o@1|f|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@58| +0#0000001#ffd7ff255|d|n|i|k|o@1|f| @1|r|a|b|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@58| +0#0000001#ffd7ff255|d|n|i|k|o@1|f| @1|z|a|B|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@58| +0#0000001#ffd7ff255|d|n|i|k|o@1|f| |a|l|a|b|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |9| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_highlights_06a.dump b/src/testdir/dumps/Test_pum_highlights_06a.dump
new file mode 100644
index 0000000..5d51f11
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_06a.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@71> |你*&
+| +0#4040ff13&@59| +0#0000001#e0e0e08@10|好*&|你*0#00e0e07&
+| +0#4040ff13#ffffff0@59| +0#0000001#ffd7ff255@8|吗*&|好|你*0#0000e05&
+| +0#4040ff13#ffffff0@59| +0#0000001#ffd7ff255@6|吗*&|好|不|你*0#0000e05&
+| +0#4040ff13#ffffff0@59| +0#0000001#ffd7ff255@6|吗*&|好|可|你*0#0000e05&
+| +0#4040ff13#ffffff0@73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |9| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_highlights_06b.dump b/src/testdir/dumps/Test_pum_highlights_06b.dump
new file mode 100644
index 0000000..41314ab
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_06b.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@69> |吗*&|你
+| +0#4040ff13&@59| +0#0000001#e0e0e08@8|吗*0#00e0e07&|好*0#0000001&|你*0#00e0e07&
+| +0#4040ff13#ffffff0@59| +0#0000001#ffd7ff255@6|吗*0#0000e05&|好*0#0000001&|不|你*0#0000e05&
+| +0#4040ff13#ffffff0@59| +0#0000001#ffd7ff255@6|吗*0#0000e05&|好*0#0000001&|可|你*0#0000e05&
+| +0#4040ff13#ffffff0@73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |9| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_highlights_07.dump b/src/testdir/dumps/Test_pum_highlights_07.dump
new file mode 100644
index 0000000..77d87d4
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_07.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o> @72
+|f+0#00e0e07#e0e0e08|o|o+0#0000001&| @4|f|o@1|k|i|n|d| | +0#4040ff13#ffffff0@58
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|f|o@1| @1|f|o@1|k|i|n|d| | +0#4040ff13#ffffff0@58
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|b|a|r| @1|f|o@1|k|i|n|d| | +0#4040ff13#ffffff0@58
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|B|a|z| @1|f|o@1|k|i|n|d| | +0#4040ff13#ffffff0@58
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|b|a|l|a| |f|o@1|k|i|n|d| | +0#4040ff13#ffffff0@58
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |9| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_highlights_08.dump b/src/testdir/dumps/Test_pum_highlights_08.dump
new file mode 100644
index 0000000..3eb1524
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_08.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@71> |o|f
+| +0#4040ff13&@58| +0#0000001#e0e0e08|d|n|i|k|o@1|f| @4|o|o+0#00e0e07&|f
+| +0#4040ff13#ffffff0@58| +0#0000001#ffd7ff255|d|n|i|k|o@1|f| @1|o@1|f|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@58| +0#0000001#ffd7ff255|d|n|i|k|o@1|f| @1|r|a|b|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@58| +0#0000001#ffd7ff255|d|n|i|k|o@1|f| @1|z|a|B|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@58| +0#0000001#ffd7ff255|d|n|i|k|o@1|f| |a|l|a|b|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |9| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_highlights_09.dump b/src/testdir/dumps/Test_pum_highlights_09.dump
new file mode 100644
index 0000000..3616c80
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_09.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0> @73
+|f+0#00e0e07#e0e0e08|o+0#0000001&@1| @11| +0#4040ff13#ffffff0@59
+|F+0#0000e05#ffd7ff255|o+0#0000001&@1|b|a|r| @8| +0#4040ff13#ffffff0@59
+|f+0#0000e05#ffd7ff255|o+0#0000001&@1|B|a|z| @8| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |I|N|S|E|R|T| |-@1| +0&&@62
diff --git a/src/testdir/dumps/Test_smooth_long_6.dump b/src/testdir/dumps/Test_smooth_long_6.dump
index 507aa46..ba48c28 100644
--- a/src/testdir/dumps/Test_smooth_long_6.dump
+++ b/src/testdir/dumps/Test_smooth_long_6.dump
@@ -3,4 +3,4 @@
|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e
|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w
-|:|s|e|t| |s|c|r|o|l@1|o| @9|3|,|9|0| @9|6@1|%|
+| @21|3|,|9|0| @9|6@1|%|
diff --git a/src/testdir/dumps/Test_smooth_long_7.dump b/src/testdir/dumps/Test_smooth_long_7.dump
index 225207f..222e001 100644
--- a/src/testdir/dumps/Test_smooth_long_7.dump
+++ b/src/testdir/dumps/Test_smooth_long_7.dump
@@ -3,4 +3,4 @@
|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o
|f| |t|e|x|t| |w|i>t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e
|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w|i|t|h| |l|o|t|s| |o|f| |t|e|x|t| |w
-|:|s|e|t| |s|c|r|o|l@1|o| @9|3|,|1|7|0| @8|6@1|%|
+| @21|3|,|1|7|0| @8|6@1|%|
diff --git a/src/testdir/dumps/Test_smooth_long_scrolloff_1.dump b/src/testdir/dumps/Test_smooth_long_scrolloff_1.dump
new file mode 100644
index 0000000..6c1d223
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_long_scrolloff_1.dump
@@ -0,0 +1,8 @@
+| +0&#ffffff0@39
+@40
+@40
+> @39
+@40
+@40
+@40
+@40
diff --git a/src/testdir/dumps/Test_smooth_long_scrolloff_2.dump b/src/testdir/dumps/Test_smooth_long_scrolloff_2.dump
new file mode 100644
index 0000000..9162df0
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_long_scrolloff_2.dump
@@ -0,0 +1,8 @@
+|<+0#4040ff13#ffffff0@2|t+0#0000000&|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t
+|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l
+|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g|
+|t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o|
+>l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g
+| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o
+| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n
+| @39
diff --git a/src/testdir/dumps/Test_smooth_long_scrolloff_3.dump b/src/testdir/dumps/Test_smooth_long_scrolloff_3.dump
new file mode 100644
index 0000000..1a1fcae
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_long_scrolloff_3.dump
@@ -0,0 +1,8 @@
+|<+0#4040ff13#ffffff0@2|l+0#0000000&|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l
+|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g|
+|t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o|
+|l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g
+> |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o
+| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n
+|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| @20
+@40
diff --git a/src/testdir/dumps/Test_smooth_long_scrolloff_4.dump b/src/testdir/dumps/Test_smooth_long_scrolloff_4.dump
new file mode 100644
index 0000000..4ed62b6
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_long_scrolloff_4.dump
@@ -0,0 +1,8 @@
+|<+0#4040ff13#ffffff0@2|l+0#0000000&|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l
+|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g|
+|t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o|
+|l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g
+| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o
+> |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n
+|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| @20
+@40
diff --git a/src/testdir/dumps/Test_smooth_long_scrolloff_5.dump b/src/testdir/dumps/Test_smooth_long_scrolloff_5.dump
new file mode 100644
index 0000000..6d7e157
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_long_scrolloff_5.dump
@@ -0,0 +1,8 @@
+|<+0#4040ff13#ffffff0@2|l+0#0000000&|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l
+|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g|
+|t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o|
+|l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g
+| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o
+| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n
+>g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| @20
+@40
diff --git a/src/testdir/dumps/Test_smooth_long_scrolloff_6.dump b/src/testdir/dumps/Test_smooth_long_scrolloff_6.dump
new file mode 100644
index 0000000..4f5dcea
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_long_scrolloff_6.dump
@@ -0,0 +1,8 @@
+|<+0#4040ff13#ffffff0@2| +0#0000000&|l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o|
+|l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g
+| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o
+| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n
+|g| |t|w|o| |l|o|n|g| |t|w|o| |l|o|n|g| @20
+>t|h|r|e@1| @34
+|f|o|u|r| @35
+@40
diff --git a/src/testdir/dumps/Test_smooth_long_scrolloff_7.dump b/src/testdir/dumps/Test_smooth_long_scrolloff_7.dump
new file mode 100644
index 0000000..c21c022
--- /dev/null
+++ b/src/testdir/dumps/Test_smooth_long_scrolloff_7.dump
@@ -0,0 +1,8 @@
+| +0&#ffffff0@39
+@40
+@40
+@40
+@40
+@40
+> @39
+@40
diff --git a/src/testdir/dumps/Test_smoothscroll_in_qf_window_1.dump b/src/testdir/dumps/Test_smoothscroll_in_qf_window_1.dump
new file mode 100644
index 0000000..efdec1e
--- /dev/null
+++ b/src/testdir/dumps/Test_smoothscroll_in_qf_window_1.dump
@@ -0,0 +1,20 @@
+> +0&#ffffff0@59
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|[+3#0000000&|N|o| |N|a|m|e|]| @50
+|<+0#4040ff13&@2| +0#af5f00255&|2+0#0000000&|1| |2@1| |2|3| |2|4| |2|5| |2|6| |2|7| |2|8| |2|9| @29
+| +0#af5f00255&|1|0| ||+0#0000000&@1| |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |1|0| |1@1| |1|2| |1|3| |1|4| |1|5| |1|6| |1|7| |1|8| |1|9| |2|0|
+| +0#af5f00255&@3|2+0#0000000&|1| |2@1| |2|3| |2|4| |2|5| |2|6| |2|7| |2|8| |2|9| @29
+| +0#af5f00255&|1@1| ||+0#0000000&@1| |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |1|0| |1@1| |1|2| |1|3| |1|4| |1|5| |1|6| |1|7| |1|8| |1|9| |2|0|
+| +0#af5f00255&@3|2+0#0000000&|1| |2@1| |2|3| |2|4| |2|5| |2|6| |2|7| |2|8| |2|9| @29
+|[+1&&|Q|u|i|c|k|f|i|x| |L|i|s|t|]| @44
+| +0&&@59
diff --git a/src/testdir/dumps/Test_smoothscroll_in_qf_window_2.dump b/src/testdir/dumps/Test_smoothscroll_in_qf_window_2.dump
new file mode 100644
index 0000000..200df91
--- /dev/null
+++ b/src/testdir/dumps/Test_smoothscroll_in_qf_window_2.dump
@@ -0,0 +1,20 @@
+> +0&#ffffff0@59
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|[+3#0000000&|N|o| |N|a|m|e|]| @50
+| +0#af5f00255&@1|1| | +0#0000000&@55
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|~| @58
+|[+1#0000000&|Q|u|i|c|k|f|i|x| |L|i|s|t|]| @44
+|:+0&&|c|a|l@1| |s|e|t|q|f|l|i|s|t|(|[|]|,| |'|r|'|)| @35
diff --git a/src/testdir/dumps/Test_smoothscroll_in_qf_window_3.dump b/src/testdir/dumps/Test_smoothscroll_in_qf_window_3.dump
new file mode 100644
index 0000000..54b726f
--- /dev/null
+++ b/src/testdir/dumps/Test_smoothscroll_in_qf_window_3.dump
@@ -0,0 +1,20 @@
+> +0&#ffffff0@59
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|[+3#0000000&|N|o| |N|a|m|e|]| @50
+| +0#af5f00255&@1|1| ||+0#0000000#ffff4012@1| |f|o@1| @49
+| +0#af5f00255#ffffff0@1|2| ||+0#0000000&@1| |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |1|0| |1@1| |1|2| |1|3| |1|4| |1|5| |1|6| |1|7| |1|8| |1|9| |2|0|
+| +0#af5f00255&@3|2+0#0000000&|1| |2@1| |2|3| |2|4| |2|5| |2|6| |2|7| |2|8| |2|9| @29
+| +0#af5f00255&@1|3| ||+0#0000000&@1| |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |1|0| |1@1| |1|2| |1|3| |1|4| |1|5| |1|6| |1|7| |1|8| |1|9| |2|0|
+| +0#af5f00255&@3|2+0#0000000&|1| |2@1| |2|3| |2|4| |2|5| |2|6| |2|7| |2|8| |2|9| @29
+|[+1&&|Q|u|i|c|k|f|i|x| |L|i|s|t|]| @44
+|:+0&&|c|a|l@1| |s|e|t|q|f|l|i|s|t|(|g|:|l|,| |'|r|'|)| @34
diff --git a/src/testdir/dumps/Test_smoothscroll_in_qf_window_4.dump b/src/testdir/dumps/Test_smoothscroll_in_qf_window_4.dump
new file mode 100644
index 0000000..1a4cdd2
--- /dev/null
+++ b/src/testdir/dumps/Test_smoothscroll_in_qf_window_4.dump
@@ -0,0 +1,20 @@
+> +0&#ffffff0@59
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|[+3#0000000&|N|o| |N|a|m|e|]| @50
+| +0#af5f00255&@1|1| ||+0#0000000#ffff4012@1| |0| |1| |2| |3| |4| |5| |6| |7| |8| |9| |1|0| |1@1| |1|2| |1|3| |1|4| |1|5| |1|6| |1|7| |1|8| |1|9| |2|0|
+| +0#af5f00255#ffffff0@3|2+0#0000000#ffff4012|1| |2@1| |2|3| |2|4| |2|5| |2|6| |2|7| |2|8| |2|9| |3|0| |3|1| |3|2| |3@1| |3|4| |3|5| |3|6| |3|7| |3|8| |3|9
+| +0#af5f00255#ffffff0@3| +0#0000000#ffff4012|4|0| |4|1| |4|2| |4|3| |4@1| |4|5| |4|6| |4|7| |4|8| |4|9| |5|0| |5|1| |5|2| |5|3| |5|4| |5@1| |5|6| |5|7| |5
+| +0#af5f00255#ffffff0@3|8+0#0000000#ffff4012| |5|9| |6|0| |6|1| |6|2| |6|3| |6|4| |6|5| |6@1| |6|7| |6|8| |6|9| |7|0| |7|1| |7|2| |7|3| |7|4| |7|5| |7|6|
+| +0#af5f00255#ffffff0@3|7+0#0000000#ffff4012@1| |7|8| |7|9| |8|0| |8|1| |8|2| |8|3| |8|4| |8|5| |8|6| |8|7| |8@1| |8|9| |9|0| |9|1| |9|2| |9|3| |9|4| |9|5
+|[+1&#ffffff0|Q|u|i|c|k|f|i|x| |L|i|s|t|]| @44
+|:+0&&|c|a|l@1| |s|e|t|q|f|l|i|s|t|(|g|:|l|1|,| |'|r|'|)| @33
diff --git a/src/testdir/dumps/Test_smoothscroll_in_qf_window_5.dump b/src/testdir/dumps/Test_smoothscroll_in_qf_window_5.dump
new file mode 100644
index 0000000..fcc2106
--- /dev/null
+++ b/src/testdir/dumps/Test_smoothscroll_in_qf_window_5.dump
@@ -0,0 +1,20 @@
+> +0&#ffffff0@59
+|~+0#4040ff13&| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|~| @58
+|[+3#0000000&|N|o| |N|a|m|e|]| @50
+|<+0#4040ff13&@2| +0#af5f00255&| +0#0000000#ffff4012|9|3|7| |9|3|8| |9|3|9| |9|4|0| |9|4|1| |9|4|2| |9|4|3| |9|4@1| |9|4|5| |9|4|6| |9|4|7| |9|4|8| |9|4|9| |9|5|0
+| +0#af5f00255#ffffff0@3| +0#0000000#ffff4012|9|5|1| |9|5|2| |9|5|3| |9|5|4| |9|5@1| |9|5|6| |9|5|7| |9|5|8| |9|5|9| |9|6|0| |9|6|1| |9|6|2| |9|6|3| |9|6|4
+| +0#af5f00255#ffffff0@3| +0#0000000#ffff4012|9|6|5| |9|6@1| |9|6|7| |9|6|8| |9|6|9| |9|7|0| |9|7|1| |9|7|2| |9|7|3| |9|7|4| |9|7|5| |9|7|6| |9|7@1| |9|7|8
+| +0#af5f00255#ffffff0@3| +0#0000000#ffff4012|9|7|9| |9|8|0| |9|8|1| |9|8|2| |9|8|3| |9|8|4| |9|8|5| |9|8|6| |9|8|7| |9|8@1| |9|8|9| |9@1|0| |9@1|1| |9@1|2
+| +0#af5f00255#ffffff0@3| +0#0000000#ffff4012|9@1|3| |9@1|4| |9@1|5| |9@1|6| |9@1|7| |9@1|8| |9@2| @27
+|[+1&#ffffff0|Q|u|i|c|k|f|i|x| |L|i|s|t|]| @44
+|:+0&&|c|a|l@1| |s|e|t|q|f|l|i|s|t|(|g|:|l|1|,| |'|r|'|)| @33
diff --git a/src/testdir/dumps/Test_wildmenu_pum_hl_match_1.dump b/src/testdir/dumps/Test_wildmenu_pum_hl_match_1.dump
new file mode 100644
index 0000000..0772682
--- /dev/null
+++ b/src/testdir/dumps/Test_wildmenu_pum_hl_match_1.dump
@@ -0,0 +1,10 @@
+| +0&#ffffff0@49
+|~+0#4040ff13&| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @3| +0#0000001#e0e0e08|p+0#00e0e07&|l|a+0#0000001&|c+0#00e0e07&|e+0#0000001&| @9| +0#4040ff13#ffffff0@28
+|~| @3| +0#0000001#ffd7ff255|u|n|p+0#0000e05&|l|a+0#0000001&|c+0#0000e05&|e+0#0000001&| @7| +0#4040ff13#ffffff0@28
+|:+0#0000000&|s|i|g|n| |p|l|a|c|e> @38
diff --git a/src/testdir/dumps/Test_wildmenu_pum_hl_match_2.dump b/src/testdir/dumps/Test_wildmenu_pum_hl_match_2.dump
new file mode 100644
index 0000000..ec781ef
--- /dev/null
+++ b/src/testdir/dumps/Test_wildmenu_pum_hl_match_2.dump
@@ -0,0 +1,10 @@
+| +0&#ffffff0@49
+|~+0#4040ff13&| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @3| +0#0000001#ffd7ff255|p+0#0000e05&|l|a+0#0000001&|c+0#0000e05&|e+0#0000001&| @9| +0#4040ff13#ffffff0@28
+|~| @3| +0#0000001#e0e0e08|u|n|p+0#00e0e07&|l|a+0#0000001&|c+0#00e0e07&|e+0#0000001&| @7| +0#4040ff13#ffffff0@28
+|:+0#0000000&|s|i|g|n| |u|n|p|l|a|c|e> @36
diff --git a/src/testdir/dumps/Test_wildmenu_pum_hl_match_3.dump b/src/testdir/dumps/Test_wildmenu_pum_hl_match_3.dump
new file mode 100644
index 0000000..afb792a
--- /dev/null
+++ b/src/testdir/dumps/Test_wildmenu_pum_hl_match_3.dump
@@ -0,0 +1,10 @@
+| +0&#ffffff0@49
+|~+0#4040ff13&| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @3| +0#0000001#ffd7ff255|p+0#0000e05&|l|a+0#0000001&|c+0#0000e05&|e+0#0000001&| @9| +0#4040ff13#ffffff0@28
+|~| @3| +0#0000001#ffd7ff255|u|n|p+0#0000e05&|l|a+0#0000001&|c+0#0000e05&|e+0#0000001&| @7| +0#4040ff13#ffffff0@28
+|:+0#0000000&|s|i|g|n| |p|l|c> @40
diff --git a/src/testdir/dumps/Test_wildmenu_pum_hl_match_4.dump b/src/testdir/dumps/Test_wildmenu_pum_hl_match_4.dump
new file mode 100644
index 0000000..0be8053
--- /dev/null
+++ b/src/testdir/dumps/Test_wildmenu_pum_hl_match_4.dump
@@ -0,0 +1,10 @@
+| +0&#ffffff0@49
+|~+0#4040ff13&| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @3| +0#0000001#e0e0e08|u+0#00e0e07&|n|d+0#0000001&|e|f|i|n|e| @6| +0#4040ff13#ffffff0@28
+|~| @3| +0#0000001#ffd7ff255|u+0#0000e05&|n|p+0#0000001&|l|a|c|e| @7| +0#4040ff13#ffffff0@28
+|:+0#0000000&|s|i|g|n| |u|n|d|e|f|i|n|e> @35
diff --git a/src/testdir/dumps/Test_wildmenu_pum_hl_match_5.dump b/src/testdir/dumps/Test_wildmenu_pum_hl_match_5.dump
new file mode 100644
index 0000000..7453cb9
--- /dev/null
+++ b/src/testdir/dumps/Test_wildmenu_pum_hl_match_5.dump
@@ -0,0 +1,10 @@
+| +0&#ffffff0@49
+|~+0#4040ff13&| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @3| +0#0000001#ffd7ff255|u+0#0000e05&|n|d+0#0000001&|e|f|i|n|e| @6| +0#4040ff13#ffffff0@28
+|~| @3| +0#0000001#e0e0e08|u+0#00e0e07&|n|p+0#0000001&|l|a|c|e| @7| +0#4040ff13#ffffff0@28
+|:+0#0000000&|s|i|g|n| |u|n|p|l|a|c|e> @36
diff --git a/src/testdir/dumps/Test_wildmenu_pum_hl_match_6.dump b/src/testdir/dumps/Test_wildmenu_pum_hl_match_6.dump
new file mode 100644
index 0000000..8345007
--- /dev/null
+++ b/src/testdir/dumps/Test_wildmenu_pum_hl_match_6.dump
@@ -0,0 +1,10 @@
+| +0&#ffffff0@49
+|~+0#4040ff13&| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @48
+|~| @3| +0#0000001#ffd7ff255|u+0#0000e05&|n|d+0#0000001&|e|f|i|n|e| @6| +0#4040ff13#ffffff0@28
+|~| @3| +0#0000001#ffd7ff255|u+0#0000e05&|n|p+0#0000001&|l|a|c|e| @7| +0#4040ff13#ffffff0@28
+|:+0#0000000&|s|i|g|n| |u|n> @41
diff --git a/src/testdir/dumps/Test_wildmenu_pum_rl.dump b/src/testdir/dumps/Test_wildmenu_pum_rl.dump
new file mode 100644
index 0000000..55db4ca
--- /dev/null
+++ b/src/testdir/dumps/Test_wildmenu_pum_rl.dump
@@ -0,0 +1,10 @@
+| +0&#ffffff0@49
+| +0#4040ff13&@48|~
+| @48|~
+| @4| +0#0000001#e0e0e08|d|e|f|i|n|e| @8| +0#4040ff13#ffffff0@27|~
+| @4| +0#0000001#ffd7ff255|j|u|m|p| @10| +0#4040ff13#ffffff0@27|~
+| @4| +0#0000001#ffd7ff255|l|i|s|t| @10| +0#4040ff13#ffffff0@27|~
+| @4| +0#0000001#ffd7ff255|p|l|a|c|e| @9| +0#4040ff13#ffffff0@27|~
+| @4| +0#0000001#ffd7ff255|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@27|~
+| @4| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@27|~
+|:+0#0000000&|s|i|g|n| |d|e|f|i|n|e> @37
diff --git a/src/testdir/gen_opt_test.vim b/src/testdir/gen_opt_test.vim
index a195bca..8cca2b9 100644
--- a/src/testdir/gen_opt_test.vim
+++ b/src/testdir/gen_opt_test.vim
@@ -76,7 +76,7 @@ let test_values = {
\ 'clipboard': [['', 'unnamed', 'autoselect,unnamed', 'html', 'exclude:vimdisplay'], ['xxx', '\ze*', 'exclude:\\%(']],
\ 'colorcolumn': [['', '8', '+2'], ['xxx']],
\ 'comments': [['', 'b:#'], ['xxx']],
- \ 'commentstring': [['', '/*%s*/'], ['xxx']],
+ \ 'commentstring': [['', '/*\ %s\ */'], ['xxx']],
\ 'complete': [['', 'w,b'], ['xxx']],
\ 'concealcursor': [['', 'n', 'nvic'], ['xxx']],
\ 'completeopt': [['', 'menu', 'menu,longest'], ['xxx', 'menu,,,longest,']],
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index 24e1daf..c9f257a 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -4024,6 +4024,32 @@ func Test_autocmd_get()
\ event: 'BufAdd', pattern: '*.abc'}))
call assert_equal([], autocmd_get(#{group: 'TestAutoCmdFns',
\ event: 'BufWipeout'}))
+
+ " Test for getting autocmds after removing one inside an autocmd
+ func CheckAutocmdGet()
+ augroup TestAutoCmdFns
+ autocmd! BufAdd *.vim
+ augroup END
+
+ let expected = [
+ \ #{cmd: 'echo "bufadd-py"', group: 'TestAutoCmdFns',
+ \ pattern: '*.py', nested: v:false, once: v:false,
+ \ event: 'BufAdd'},
+ \ #{cmd: 'echo "bufhidden"', group: 'TestAutoCmdFns',
+ \ pattern: '*.vim', nested: v:false,
+ \ once: v:false, event: 'BufHidden'}]
+
+ call assert_equal(expected, autocmd_get(#{group: 'TestAutoCmdFns'}))
+ call assert_equal([expected[0]],
+ \ autocmd_get(#{group: 'TestAutoCmdFns', pattern: '*.py'}))
+ call assert_equal([expected[1]],
+ \ autocmd_get(#{group: 'TestAutoCmdFns', pattern: '*.vim'}))
+ endfunc
+
+ autocmd User Xauget call CheckAutocmdGet()
+ doautocmd User Xauget
+ autocmd! User Xauget
+
call assert_fails("call autocmd_get(#{group: 'abc', event: 'BufAdd'})",
\ 'E367:')
let cmd = "echo autocmd_get(#{group: 'TestAutoCmdFns', event: 'abc'})"
diff --git a/src/testdir/test_autoload.vim b/src/testdir/test_autoload.vim
index 835b81e..d0f9136 100644
--- a/src/testdir/test_autoload.vim
+++ b/src/testdir/test_autoload.vim
@@ -26,5 +26,4 @@ func Test_autoload_vim9script()
call assert_equal(49, auto9#Add42(7))
endfunc
-
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_balloon.vim b/src/testdir/test_balloon.vim
index 5e84f9e..80d5831 100644
--- a/src/testdir/test_balloon.vim
+++ b/src/testdir/test_balloon.vim
@@ -64,4 +64,29 @@ func Test_balloon_eval_term_visual()
call StopVimInTerminal(buf)
endfunc
+func Test_balloon_eval_term_rightleft()
+ CheckFeature rightleft
+
+ " Use <Ignore> after <MouseMove> to return from vgetc() without removing
+ " the balloon.
+ let xtra_lines =<< trim [CODE]
+ set rightleft
+ func Trigger()
+ call test_setmouse(2, 50 + 1 - 6)
+ call feedkeys("\<MouseMove>\<Ignore>", "xt")
+ endfunc
+ [CODE]
+ call writefile(s:common_script + xtra_lines, 'XTest_beval_rl', 'D')
+
+ " Check that the balloon shows up after a mouse move
+ let buf = RunVimInTerminal('-S XTest_beval_rl', {'rows': 10, 'cols': 50})
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, 'll')
+ call term_sendkeys(buf, ":call Trigger()\<CR>")
+ call VerifyScreenDump(buf, 'Test_balloon_eval_term_03', {})
+
+ " clean up
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_blob.vim b/src/testdir/test_blob.vim
index cccecb7..f0e8209 100644
--- a/src/testdir/test_blob.vim
+++ b/src/testdir/test_blob.vim
@@ -74,6 +74,13 @@ func Test_blob_assign()
VAR l = [0z12]
VAR m = deepcopy(l)
LET m[0] = 0z34 #" E742 or E741 should not occur.
+
+ VAR blob1 = 0z10
+ LET blob1 += test_null_blob()
+ call assert_equal(0z10, blob1)
+ LET blob1 = test_null_blob()
+ LET blob1 += 0z20
+ call assert_equal(0z20, blob1)
END
call v9.CheckLegacyAndVim9Success(lines)
@@ -97,6 +104,18 @@ func Test_blob_assign()
let lines =<< trim END
VAR b = 0zDEADBEEF
+ LET b[0 : 1] = 0x1122
+ END
+ call v9.CheckLegacyAndVim9Failure(lines, ['E709:', 'E1012:', 'E709:'])
+
+ let lines =<< trim END
+ VAR b = 0zDEADBEEF
+ LET b[0] = 0z11
+ END
+ call v9.CheckLegacyAndVim9Failure(lines, ['E974:', 'E974:', 'E1012:'])
+
+ let lines =<< trim END
+ VAR b = 0zDEADBEEF
LET b ..= 0z33
END
call v9.CheckLegacyAndVim9Failure(lines, ['E734:', 'E1019:', 'E734:'])
@@ -331,6 +350,17 @@ func Test_blob_for_loop()
call assert_equal(5, i)
END
call v9.CheckLegacyAndVim9Success(lines)
+
+ " Test for skipping the loop var assignment in a for loop
+ let lines =<< trim END
+ VAR blob = 0z998877
+ VAR c = 0
+ for _ in blob
+ LET c += 1
+ endfor
+ call assert_equal(3, c)
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
endfunc
func Test_blob_concatenate()
@@ -819,6 +849,7 @@ func Test_indexof()
call assert_equal(-1, indexof(test_null_blob(), "v:val == 0xde"))
call assert_equal(-1, indexof(b, test_null_string()))
call assert_equal(-1, indexof(b, test_null_function()))
+ call assert_equal(-1, indexof(b, ""))
let b = 0z01020102
call assert_equal(1, indexof(b, "v:val == 0x02", #{startidx: 0}))
@@ -830,6 +861,7 @@ func Test_indexof()
" failure cases
call assert_fails('let i = indexof(b, "val == 0xde")', 'E121:')
call assert_fails('let i = indexof(b, {})', 'E1256:')
+ call assert_fails('let i = indexof(b, " ")', 'E15:')
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim
index ed8f89d..8b7489e 100644
--- a/src/testdir/test_cmdline.vim
+++ b/src/testdir/test_cmdline.vim
@@ -2741,6 +2741,55 @@ func Test_wildmenu_pum_odd_wildchar()
call StopVimInTerminal(buf)
endfunc
+" Test that 'rightleft' should not affect cmdline completion popup menu.
+func Test_wildmenu_pum_rightleft()
+ CheckFeature rightleft
+ CheckScreendump
+
+ let lines =<< trim END
+ set wildoptions=pum
+ set rightleft
+ END
+ call writefile(lines, 'Xwildmenu_pum_rl', 'D')
+ let buf = RunVimInTerminal('-S Xwildmenu_pum_rl', #{rows: 10, cols: 50})
+
+ call term_sendkeys(buf, ":sign \<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_rl', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
+" Test highlighting matched text in cmdline completion popup menu.
+func Test_wildmenu_pum_hl_match()
+ CheckScreendump
+
+ let lines =<< trim END
+ set wildoptions=pum,fuzzy
+ hi PmenuMatchSel ctermfg=6 ctermbg=7
+ hi PmenuMatch ctermfg=4 ctermbg=225
+ END
+ call writefile(lines, 'Xwildmenu_pum_hl', 'D')
+ let buf = RunVimInTerminal('-S Xwildmenu_pum_hl', #{rows: 10, cols: 50})
+
+ call term_sendkeys(buf, ":sign plc\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_hl_match_1', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_hl_match_2', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_hl_match_3', {})
+ call term_sendkeys(buf, "\<Esc>:set wildoptions-=fuzzy\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":sign un\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_hl_match_4', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_hl_match_5', {})
+ call term_sendkeys(buf, "\<Tab>")
+ call VerifyScreenDump(buf, 'Test_wildmenu_pum_hl_match_6', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ call StopVimInTerminal(buf)
+endfunc
+
" Test for completion after a :substitute command followed by a pipe (|)
" character
func Test_cmdline_complete_substitute()
diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim
index 789e44c..1665330 100644
--- a/src/testdir/test_edit.vim
+++ b/src/testdir/test_edit.vim
@@ -1952,6 +1952,11 @@ func Test_edit_insert_reg()
let @r = 'sample'
call feedkeys("a\<C-R>=SaveFirstLine()\<CR>", "xt")
call assert_equal('"', g:Line)
+
+ " Test for inserting an null and an empty list
+ call feedkeys("a\<C-R>=test_null_list()\<CR>", "xt")
+ call feedkeys("a\<C-R>=[]\<CR>", "xt")
+ call assert_equal(['r'], getbufline('', 1, '$'))
call test_override('ALL', 0)
close!
endfunc
diff --git a/src/testdir/test_filecopy.vim b/src/testdir/test_filecopy.vim
new file mode 100644
index 0000000..b526dce
--- /dev/null
+++ b/src/testdir/test_filecopy.vim
@@ -0,0 +1,72 @@
+" Test filecopy()
+
+source check.vim
+source shared.vim
+
+func Test_copy_file_to_file()
+ call writefile(['foo'], 'Xcopy1')
+
+ call assert_true(filecopy('Xcopy1', 'Xcopy2'))
+
+ call assert_equal(['foo'], readfile('Xcopy2'))
+
+ " When the destination file already exists, it should not be overwritten.
+ call writefile(['foo'], 'Xcopy1')
+ call writefile(['bar'], 'Xcopy2', 'D')
+ call assert_false(filecopy('Xcopy1', 'Xcopy2'))
+ call assert_equal(['bar'], readfile('Xcopy2'))
+
+ call delete('Xcopy2')
+ call delete('Xcopy1')
+endfunc
+
+func Test_copy_symbolic_link()
+ CheckUnix
+
+ call writefile(['text'], 'Xtestfile', 'D')
+ silent !ln -s -f Xtestfile Xtestlink
+
+ call assert_true(filecopy('Xtestlink', 'Xtestlink2'))
+ call assert_equal('link', getftype('Xtestlink2'))
+ call assert_equal(['text'], readfile('Xtestlink2'))
+
+ " When the destination file already exists, it should not be overwritten.
+ call assert_false(filecopy('Xtestlink', 'Xtestlink2'))
+
+ call delete('Xtestlink2')
+ call delete('Xtestlink')
+ call delete('Xtestfile')
+endfunc
+
+func Test_copy_dir_to_dir()
+ call mkdir('Xcopydir1')
+ call writefile(['foo'], 'Xcopydir1/Xfilecopy')
+ call mkdir('Xcopydir2')
+
+ " Directory copy is not supported
+ call assert_false(filecopy('Xcopydir1', 'Xcopydir2'))
+
+ call delete('Xcopydir2', 'rf')
+ call delete('Xcopydir1', 'rf')
+endfunc
+
+func Test_copy_fails()
+ CheckUnix
+
+ call writefile(['foo'], 'Xfilecopy', 'D')
+
+ " Can't copy into a non-existing directory.
+ call assert_false(filecopy('Xfilecopy', 'Xdoesnotexist/Xfilecopy'))
+
+ " Can't copy a non-existing file.
+ call assert_false(filecopy('Xdoesnotexist', 'Xfilecopy2'))
+ call assert_equal('', glob('Xfilecopy2'))
+
+ " Can't copy to en empty file name.
+ call assert_false(filecopy('Xfilecopy', ''))
+
+ call assert_fails('call filecopy("Xfilecopy", [])', 'E1174:')
+ call assert_fails('call filecopy(0z, "Xfilecopy")', 'E1174:')
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim
index f8fe9ec..d0a078f 100644
--- a/src/testdir/test_filetype.vim
+++ b/src/testdir/test_filetype.vim
@@ -131,7 +131,7 @@ def s:GetFilenameChecks(): dict<list<string>>
bst: ['file.bst'],
bzl: ['file.bazel', 'file.bzl', 'WORKSPACE', 'WORKSPACE.bzlmod'],
bzr: ['bzr_log.any', 'bzr_log.file'],
- c: ['enlightenment/file.cfg', 'file.qc', 'file.c', 'some-enlightenment/file.cfg'],
+ c: ['enlightenment/file.cfg', 'file.qc', 'file.c', 'some-enlightenment/file.cfg', 'file.mdh', 'file.epro'],
cabal: ['file.cabal'],
cabalconfig: ['cabal.config', expand("$HOME/.config/cabal/config")] + WhenConfigHome('$XDG_CONFIG_HOME/cabal/config'),
cabalproject: ['cabal.project', 'cabal.project.local'],
@@ -336,6 +336,7 @@ def s:GetFilenameChecks(): dict<list<string>>
htmlm4: ['file.html.m4'],
httest: ['file.htt', 'file.htb'],
hurl: ['file.hurl'],
+ hyprlang: ['hyprlock.conf', 'hyprland.conf', 'hypridle.conf', 'hyprpaper.conf'],
i3config: ['/home/user/.i3/config', '/home/user/.config/i3/config', '/etc/i3/config', '/etc/xdg/i3/config'],
ibasic: ['file.iba', 'file.ibi'],
icemenu: ['/.icewm/menu', 'any/.icewm/menu'],
@@ -344,6 +345,7 @@ def s:GetFilenameChecks(): dict<list<string>>
inform: ['file.inf', 'file.INF'],
initng: ['/etc/initng/any/file.i', 'file.ii', 'any/etc/initng/any/file.i'],
inittab: ['inittab'],
+ inko: ['file.inko'],
ipfilter: ['ipf.conf', 'ipf6.conf', 'ipf.rules'],
iss: ['file.iss'],
ist: ['file.ist', 'file.mst'],
@@ -358,10 +360,11 @@ def s:GetFilenameChecks(): dict<list<string>>
javascriptreact: ['file.jsx'],
jess: ['file.clp'],
jgraph: ['file.jgr'],
+ jj: ['file.jjdescription'],
jq: ['file.jq'],
jovial: ['file.jov', 'file.j73', 'file.jovial'],
jproperties: ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file', 'org.eclipse.xyz.prefs'],
- json: ['file.json', 'file.jsonp', 'file.json-patch', 'file.geojson', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', 'file.jupyterlab-settings', '.prettierrc', '.firebaserc', '.stylelintrc', 'file.slnf', 'file.sublime-project', 'file.sublime-settings', 'file.sublime-workspace', 'file.bd', 'file.bda', 'file.xci', 'flake.lock'],
+ json: ['file.json', 'file.jsonp', 'file.json-patch', 'file.geojson', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', 'file.jupyterlab-settings', '.prettierrc', '.firebaserc', '.stylelintrc', '.lintstagedrc', 'file.slnf', 'file.sublime-project', 'file.sublime-settings', 'file.sublime-workspace', 'file.bd', 'file.bda', 'file.xci', 'flake.lock'],
json5: ['file.json5'],
jsonc: ['file.jsonc', '.babelrc', '.eslintrc', '.jsfmtrc', '.jshintrc', '.jscsrc', '.vsconfig', '.hintrc', '.swrc', 'jsconfig.json', 'tsconfig.json', 'tsconfig.test.json', 'tsconfig-test.json', '.luaurc'],
jsonl: ['file.jsonl'],
@@ -417,7 +420,7 @@ def s:GetFilenameChecks(): dict<list<string>>
mail: ['snd.123', '.letter', '.letter.123', '.followup', '.article', '.article.123', 'pico.123', 'mutt-xx-xxx', 'muttng-xx-xxx', 'ae123.txt', 'file.eml', 'reportbug-file'],
mailaliases: ['/etc/mail/aliases', '/etc/aliases', 'any/etc/aliases', 'any/etc/mail/aliases'],
mailcap: ['.mailcap', 'mailcap'],
- make: ['file.mk', 'file.mak', 'file.dsp', 'makefile', 'Makefile', 'makefile-file', 'Makefile-file', 'some-makefile', 'some-Makefile'],
+ make: ['file.mk', 'file.mak', 'file.dsp', 'makefile', 'Makefile', 'makefile-file', 'Makefile-file', 'some-makefile', 'some-Makefile', 'Kbuild'],
mallard: ['file.page'],
man: ['file.man'],
manconf: ['/etc/man.conf', 'man.config', 'any/etc/man.conf'],
@@ -574,6 +577,7 @@ def s:GetFilenameChecks(): dict<list<string>>
psl: ['file.psl'],
pug: ['file.pug'],
puppet: ['file.pp'],
+ purescript: ['file.purs'],
pymanifest: ['MANIFEST.in'],
pyret: ['file.arr'],
pyrex: ['file.pyx', 'file.pxd'],
@@ -588,6 +592,7 @@ def s:GetFilenameChecks(): dict<list<string>>
radiance: ['file.rad', 'file.mat'],
raku: ['file.pm6', 'file.p6', 'file.t6', 'file.pod6', 'file.raku', 'file.rakumod', 'file.rakudoc', 'file.rakutest'],
raml: ['file.raml'],
+ rasi: ['file.rasi'],
ratpoison: ['.ratpoisonrc', 'ratpoisonrc'],
rbs: ['file.rbs'],
rc: ['file.rc', 'file.rch'],
@@ -640,7 +645,7 @@ def s:GetFilenameChecks(): dict<list<string>>
sh: ['.bashrc', '.bash_profile', '.bash-profile', '.bash_logout', '.bash-logout', '.bash_aliases', '.bash-aliases', '.bash_history', '.bash-history',
'/tmp/bash-fc-3Ozjlw', '/tmp/bash-fc.3Ozjlw', 'PKGBUILD', 'APKBUILD', 'file.bash', '/usr/share/doc/bash-completion/filter.sh',
'/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf', 'file.bats', '.ash_history', 'any/etc/neofetch/config.conf', '.xprofile',
- 'user-dirs.defaults', 'user-dirs.dirs', 'makepkg.conf', '.makepkg.conf'],
+ 'user-dirs.defaults', 'user-dirs.dirs', 'makepkg.conf', '.makepkg.conf', 'file.mdd', 'file.cygport'],
sieve: ['file.siv', 'file.sieve'],
sil: ['file.sil'],
simula: ['file.sim'],
@@ -651,6 +656,7 @@ def s:GetFilenameChecks(): dict<list<string>>
slang: ['file.sl'],
sage: ['file.sage'],
slice: ['file.ice'],
+ slint: ['file.slint'],
slpconf: ['/etc/slp.conf', 'any/etc/slp.conf'],
slpreg: ['/etc/slp.reg', 'any/etc/slp.reg'],
slpspi: ['/etc/slp.spi', 'any/etc/slp.spi'],
@@ -663,6 +669,7 @@ def s:GetFilenameChecks(): dict<list<string>>
smith: ['file.smt', 'file.smith'],
smithy: ['file.smithy'],
sml: ['file.sml'],
+ snakemake: ['file.smk', 'Snakefile'],
snobol4: ['file.sno', 'file.spt'],
solidity: ['file.sol'],
solution: ['file.sln'],
@@ -686,6 +693,7 @@ def s:GetFilenameChecks(): dict<list<string>>
starlark: ['file.ipd', 'file.star', 'file.starlark'],
stata: ['file.ado', 'file.do', 'file.imata', 'file.mata'],
stp: ['file.stp'],
+ stylus: ['a.styl', 'file.stylus'],
sudoers: ['any/etc/sudoers', 'sudoers.tmp', '/etc/sudoers', 'any/etc/sudoers.d/file'],
supercollider: ['file.quark'],
surface: ['file.sface'],
@@ -746,11 +754,12 @@ def s:GetFilenameChecks(): dict<list<string>>
tcl: ['file.tcl', 'file.tm', 'file.tk', 'file.itcl', 'file.itk', 'file.jacl', '.tclshrc', 'tclsh.rc', '.wishrc', '.tclsh-history', '.xsctcmdhistory', '.xsdbcmdhistory'],
tablegen: ['file.td'],
teal: ['file.tl'],
+ templ: ['file.templ'],
template: ['file.tmpl'],
teraterm: ['file.ttl'],
terminfo: ['file.ti'],
'terraform-vars': ['file.tfvars'],
- tex: ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl', 'any/.texlive/texmf-config/tex/latex/file/file.cfg', 'file.pgf', 'file.nlo', 'file.nls', 'file.out', 'file.thm', 'file.eps_tex', 'file.pygtex', 'file.pygstyle', 'file.clo', 'file.aux', 'file.brf', 'file.ind', 'file.lof', 'file.loe', 'file.nav', 'file.vrb', 'file.ins', 'file.tikz', 'file.bbx', 'file.cbx', 'file.beamer'],
+ tex: ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl', 'any/.texlive/texmf-config/tex/latex/file/file.cfg', 'file.pgf', 'file.nlo', 'file.nls', 'file.thm', 'file.eps_tex', 'file.pygtex', 'file.pygstyle', 'file.clo', 'file.aux', 'file.brf', 'file.ind', 'file.lof', 'file.loe', 'file.nav', 'file.vrb', 'file.ins', 'file.tikz', 'file.bbx', 'file.cbx', 'file.beamer'],
texinfo: ['file.texinfo', 'file.texi', 'file.txi'],
texmf: ['texmf.cnf'],
text: ['file.text', 'file.txt', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'],
@@ -985,6 +994,7 @@ def s:GetScriptChecks(): dict<list<list<string>>>
['#!/path/regina']],
janet: [['#!/path/janet']],
dart: [['#!/path/dart']],
+ vim: [['#!/path/vim']],
}
enddef
@@ -1506,6 +1516,41 @@ func Test_git_file()
filetype off
endfunc
+func Test_haredoc_file()
+ filetype on
+ call assert_true(mkdir('foo/bar', 'pR'))
+
+ call writefile([], 'README', 'D')
+ split README
+ call assert_notequal('haredoc', &filetype)
+ bwipe!
+
+ let g:filetype_haredoc = 1
+ split README
+ call assert_notequal('haredoc', &filetype)
+ bwipe!
+
+ call writefile([], 'foo/quux.ha')
+ split README
+ call assert_equal('haredoc', &filetype)
+ bwipe!
+ call delete('foo/quux.ha')
+
+ call writefile([], 'foo/bar/baz.ha', 'D')
+ split README
+ call assert_notequal('haredoc', &filetype)
+ bwipe!
+
+ let g:haredoc_search_depth = 2
+ split README
+ call assert_equal('haredoc', &filetype)
+ bwipe!
+ unlet g:filetype_haredoc
+ unlet g:haredoc_search_depth
+
+ filetype off
+endfunc
+
func Test_hook_file()
filetype on
@@ -1684,14 +1729,14 @@ func Test_mod_file()
call assert_equal('pim', b:modula2.dialect)
bwipe!
- " Modula-2 program MODULE with priorty (and uppercase extension)
+ " Modula-2 program MODULE with priority (and uppercase extension)
call writefile(['MODULE Module2Mod [42];'], 'Xfile.MOD')
split Xfile.MOD
call assert_equal('modula2', &filetype)
call assert_equal('pim', b:modula2.dialect)
bwipe!
- " Modula-2 implementation MODULE with priorty (and uppercase extension)
+ " Modula-2 implementation MODULE with priority (and uppercase extension)
call writefile(['IMPLEMENTATION MODULE Module2Mod [42];'], 'Xfile.MOD')
split Xfile.MOD
call assert_equal('modula2', &filetype)
diff --git a/src/testdir/test_fold.vim b/src/testdir/test_fold.vim
index 3c78e62..dedc4a2 100644
--- a/src/testdir/test_fold.vim
+++ b/src/testdir/test_fold.vim
@@ -8,7 +8,73 @@ func PrepIndent(arg)
return [a:arg] + repeat(["\t".a:arg], 5)
endfu
-func Test_address_fold()
+func Test_address_fold_new_default_commentstring()
+ " Test with the new commentstring defaults, that includes padding after v9.1.464
+ new
+ call setline(1, ['int FuncName() {/* {{{ */', 1, 2, 3, 4, 5, '}/* }}} */',
+ \ 'after fold 1', 'after fold 2', 'after fold 3'])
+ setl fen fdm=marker
+ " The next commands should all copy the same part of the buffer,
+ " regardless of the addressing type, since the part to be copied
+ " is folded away
+ :1y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3', '4', '5', '}/* }}} */'], getreg(0,1,1))
+ :.y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3', '4', '5', '}/* }}} */'], getreg(0,1,1))
+ :.+y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3', '4', '5', '}/* }}} */'], getreg(0,1,1))
+ :.,.y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3', '4', '5', '}/* }}} */'], getreg(0,1,1))
+ :sil .1,.y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3', '4', '5', '}/* }}} */'], getreg(0,1,1))
+ " use silent to make E493 go away
+ :sil .+,.y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3', '4', '5', '}/* }}} */'], getreg(0,1,1))
+ :,y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3', '4', '5', '}/* }}} */'], getreg(0,1,1))
+ :,+y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3', '4', '5', '}/* }}} */','after fold 1'], getreg(0,1,1))
+ " using .+3 as second address should c opy the whole folded line + the next 3
+ " lines
+ :.,+3y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3', '4', '5', '}/* }}} */',
+ \ 'after fold 1', 'after fold 2' , 'after fold 3'], getreg(0,1,1))
+ :sil .,-2y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3', '4', '5', '}/* }}} */'], getreg(0,1,1))
+
+ " now test again with folding disabled
+ set nofoldenable
+ :1y
+ call assert_equal(['int FuncName() {/* {{{ */'], getreg(0,1,1))
+ :.y
+ call assert_equal(['int FuncName() {/* {{{ */'], getreg(0,1,1))
+ :.+y
+ call assert_equal(['1'], getreg(0,1,1) )
+ :.,.y
+ call assert_equal(['int FuncName() {/* {{{ */'], getreg(0,1,1))
+ " use silent to make E493 go away
+ :sil .1,.y
+ call assert_equal(['int FuncName() {/* {{{ */', '1'], getreg(0,1,1))
+ " use silent to make E493 go away
+ :sil .+,.y
+ call assert_equal(['int FuncName() {/* {{{ */', '1'], getreg(0,1,1))
+ :,y
+ call assert_equal(['int FuncName() {/* {{{ */'], getreg(0,1,1))
+ :,+y
+ call assert_equal(['int FuncName() {/* {{{ */', '1'], getreg(0,1,1))
+ " using .+3 as second address should c opy the whole folded line + the next 3
+ " lines
+ :.,+3y
+ call assert_equal(['int FuncName() {/* {{{ */', '1', '2', '3'], getreg(0,1,1))
+ :7
+ :sil .,-2y
+ call assert_equal(['4', '5', '}/* }}} */'], getreg(0,1,1))
+
+ quit!
+endfunc
+
+func Test_address_fold_old_default_commentstring()
+ " Test with the old commentstring defaults, before v9.1.464
new
call setline(1, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/',
\ 'after fold 1', 'after fold 2', 'after fold 3'])
@@ -719,7 +785,7 @@ func Test_fold_create_marker_in_C()
call append(0, content)
call cursor(c + 1, 1)
norm! zfG
- call assert_equal(content[c] . (c < 4 ? '{{{' : '/*{{{*/'), getline(c + 1))
+ call assert_equal(content[c] . (c < 4 ? '{{{' : '/* {{{ */'), getline(c + 1))
endfor
set fdm& fdl&
@@ -1601,6 +1667,63 @@ func Test_foldtext_scriptlocal_func()
delfunc s:FoldText
endfunc
+" Test for setting 'foldtext' from the modeline and executing the expression
+" in a sandbox
+func Test_foldtext_in_modeline()
+ func ModelineFoldText()
+ call feedkeys('aFoo', 'xt')
+ return "folded text"
+ endfunc
+ let lines =<< trim END
+ func T()
+ let i = 1
+ endfunc
+ " vim: foldenable foldtext=ModelineFoldText()
+ END
+ call writefile(lines, 'Xmodelinefoldtext', 'D')
+
+ set modeline modelineexpr
+ split Xmodelinefoldtext
+
+ call cursor(1, 1)
+ normal! zf3j
+ call assert_equal('folded text', foldtextresult(1))
+ call assert_equal(lines, getbufline('', 1, '$'))
+
+ bw!
+ set modeline& modelineexpr&
+ delfunc ModelineFoldText
+endfunc
+
+" Test for setting 'foldexpr' from the modeline and executing the expression
+" in a sandbox
+func Test_foldexpr_in_modeline()
+ func ModelineFoldExpr()
+ call feedkeys('aFoo', 'xt')
+ return strlen(matchstr(getline(v:lnum),'^\s*'))
+ endfunc
+ let lines =<< trim END
+ aaa
+ bbb
+ ccc
+ ccc
+ bbb
+ aaa
+ " vim: foldenable foldmethod=expr foldexpr=ModelineFoldExpr()
+ END
+ call writefile(lines, 'Xmodelinefoldexpr', 'D')
+
+ set modeline modelineexpr
+ split Xmodelinefoldexpr
+
+ call assert_equal(2, foldlevel(3))
+ call assert_equal(lines, getbufline('', 1, '$'))
+
+ bw!
+ set modeline& modelineexpr&
+ delfunc ModelineFoldExpr
+endfunc
+
" Make sure a fold containing a nested fold is split correctly when using
" foldmethod=indent
func Test_fold_split()
diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim
index f0d7385..acdb954 100644
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -3697,6 +3697,73 @@ func Test_getmousepos()
\ column: 8,
\ coladd: 21,
\ }, getmousepos())
+
+ 30vnew
+ setlocal smoothscroll number
+ call setline(1, join(range(100)))
+ exe "normal! \<C-E>"
+ call test_setmouse(1, 5)
+ call assert_equal(#{
+ \ screenrow: 1,
+ \ screencol: 5,
+ \ winid: win_getid(),
+ \ winrow: 1,
+ \ wincol: 5,
+ \ line: 1,
+ \ column: 27,
+ \ coladd: 0,
+ \ }, getmousepos())
+ call test_setmouse(2, 5)
+ call assert_equal(#{
+ \ screenrow: 2,
+ \ screencol: 5,
+ \ winid: win_getid(),
+ \ winrow: 2,
+ \ wincol: 5,
+ \ line: 1,
+ \ column: 53,
+ \ coladd: 0,
+ \ }, getmousepos())
+
+ exe "normal! \<C-E>"
+ call test_setmouse(1, 5)
+ call assert_equal(#{
+ \ screenrow: 1,
+ \ screencol: 5,
+ \ winid: win_getid(),
+ \ winrow: 1,
+ \ wincol: 5,
+ \ line: 1,
+ \ column: 53,
+ \ coladd: 0,
+ \ }, getmousepos())
+ call test_setmouse(2, 5)
+ call assert_equal(#{
+ \ screenrow: 2,
+ \ screencol: 5,
+ \ winid: win_getid(),
+ \ winrow: 2,
+ \ wincol: 5,
+ \ line: 1,
+ \ column: 79,
+ \ coladd: 0,
+ \ }, getmousepos())
+
+ vert resize 4
+ call test_setmouse(2, 2)
+ " This used to crash Vim
+ call assert_equal(#{
+ \ screenrow: 2,
+ \ screencol: 2,
+ \ winid: win_getid(),
+ \ winrow: 2,
+ \ wincol: 2,
+ \ line: 1,
+ \ column: 53,
+ \ coladd: 0,
+ \ }, getmousepos())
+
+ bwipe!
bwipe!
endfunc
@@ -3728,6 +3795,33 @@ func Test_glob()
call assert_fails("call glob('*', 0, {})", 'E728:')
endfunc
+func Test_glob2()
+ call mkdir('[XglobDir]', 'R')
+ call mkdir('abc[glob]def', 'R')
+
+ call writefile(['glob'], '[XglobDir]/Xglob')
+ call writefile(['glob'], 'abc[glob]def/Xglob')
+ if has("unix")
+ call assert_equal([], (glob('[XglobDir]/*', 0, 1)))
+ call assert_equal([], (glob('abc[glob]def/*', 0, 1)))
+ call assert_equal(['[XglobDir]/Xglob'], (glob('\[XglobDir]/*', 0, 1)))
+ call assert_equal(['abc[glob]def/Xglob'], (glob('abc\[glob]def/*', 0, 1)))
+ elseif has("win32")
+ let _sl=&shellslash
+ call assert_equal([], (glob('[XglobDir]\*', 0, 1)))
+ call assert_equal([], (glob('abc[glob]def\*', 0, 1)))
+ call assert_equal([], (glob('\[XglobDir]\*', 0, 1)))
+ call assert_equal([], (glob('abc\[glob]def\*', 0, 1)))
+ set noshellslash
+ call assert_equal(['[XglobDir]\Xglob'], (glob('[[]XglobDir]/*', 0, 1)))
+ call assert_equal(['abc[glob]def\Xglob'], (glob('abc[[]glob]def/*', 0, 1)))
+ set shellslash
+ call assert_equal(['[XglobDir]/Xglob'], (glob('[[]XglobDir]/*', 0, 1)))
+ call assert_equal(['abc[glob]def/Xglob'], (glob('abc[[]glob]def/*', 0, 1)))
+ let &shellslash=_sl
+ endif
+endfunc
+
" Test for browse()
func Test_browse()
CheckFeature browse
@@ -4039,6 +4133,8 @@ func Test_slice()
call assert_equal('', 'ὰ̳β̳́γ̳̂δ̳̃ε̳̄ζ̳̅'->slice(1, -6))
END
call v9.CheckLegacyAndVim9Success(lines)
+
+ call assert_equal(0, slice(v:true, 1))
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_global.vim b/src/testdir/test_global.vim
index 34857b2..0f72c3c 100644
--- a/src/testdir/test_global.vim
+++ b/src/testdir/test_global.vim
@@ -116,7 +116,16 @@ func Test_global_newline()
close!
endfunc
-func Test_wrong_delimiter()
+" Test :g with ? as delimiter.
+func Test_global_question_delimiter()
+ new
+ call setline(1, ['aaaaa', 'b?bbb', 'ccccc', 'ddd?d', 'eeeee'])
+ g?\??delete
+ call assert_equal(['aaaaa', 'ccccc', 'eeeee'], getline(1, '$'))
+ bwipe!
+endfunc
+
+func Test_global_wrong_delimiter()
call assert_fails('g x^bxd', 'E146:')
endfunc
diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim
index 2ff8d34..d53750f 100644
--- a/src/testdir/test_gui.vim
+++ b/src/testdir/test_gui.vim
@@ -899,7 +899,7 @@ func Test_set_term()
endfunc
func Test_windowid_variable()
- if (g:x11_based_gui && empty($WAYLAND_DISPLAY)) || has('win32')
+ if g:x11_based_gui || has('win32')
call assert_true(v:windowid > 0)
else
call assert_equal(0, v:windowid)
diff --git a/src/testdir/test_history.vim b/src/testdir/test_history.vim
index 19490f2..b288abc 100644
--- a/src/testdir/test_history.vim
+++ b/src/testdir/test_history.vim
@@ -96,6 +96,60 @@ function Test_History()
call assert_fails('history xyz', 'E488:')
call assert_fails('history ,abc', 'E488:')
call assert_fails('call histdel(":", "\\%(")', 'E53:')
+
+ " Test for filtering the history list
+ let hist_filter = execute(':filter /_\d/ :history all')->split('\n')
+ call assert_equal(20, len(hist_filter))
+ let expected = [' # cmd history',
+ \ ' 2 text_2',
+ \ ' 3 text_3',
+ \ '> 4 text_4',
+ \ ' # search history',
+ \ ' 2 text_2',
+ \ ' 3 text_3',
+ \ '> 4 text_4',
+ \ ' # expr history',
+ \ ' 2 text_2',
+ \ ' 3 text_3',
+ \ '> 4 text_4',
+ \ ' # input history',
+ \ ' 2 text_2',
+ \ ' 3 text_3',
+ \ '> 4 text_4',
+ \ ' # debug history',
+ \ ' 2 text_2',
+ \ ' 3 text_3',
+ \ '> 4 text_4']
+ call assert_equal(expected, hist_filter)
+
+ let cmds = {'c': 'cmd', 's': 'search', 'e': 'expr', 'i': 'input', 'd': 'debug'}
+ for h in sort(keys(cmds))
+ " find some items
+ let hist_filter = execute(':filter /_\d/ :history ' .. h)->split('\n')
+ call assert_equal(4, len(hist_filter))
+
+ let expected = [' # ' .. cmds[h] .. ' history',
+ \ ' 2 text_2',
+ \ ' 3 text_3',
+ \ '> 4 text_4']
+ call assert_equal(expected, hist_filter)
+
+ " Search for an item that is not there
+ let hist_filter = execute(':filter /XXXX/ :history ' .. h)->split('\n')
+ call assert_equal(1, len(hist_filter))
+
+ let expected = [' # ' .. cmds[h] .. ' history']
+ call assert_equal(expected, hist_filter)
+
+ " Invert the filter condition, find non-matches
+ let hist_filter = execute(':filter! /_3$/ :history ' .. h)->split('\n')
+ call assert_equal(3, len(hist_filter))
+
+ let expected = [' # ' .. cmds[h] .. ' history',
+ \ ' 2 text_2',
+ \ '> 4 text_4']
+ call assert_equal(expected, hist_filter)
+ endfor
endfunction
function Test_history_truncates_long_entry()
diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim
index eb89a15..48589ce 100644
--- a/src/testdir/test_ins_complete.vim
+++ b/src/testdir/test_ins_complete.vim
@@ -630,14 +630,14 @@ func Test_pum_with_preview_win()
CheckScreendump
let lines =<< trim END
- funct Omni_test(findstart, base)
- if a:findstart
- return col(".") - 1
- endif
- return [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "three", info: "3info"}]
- endfunc
- set omnifunc=Omni_test
- set completeopt+=longest
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".") - 1
+ endif
+ return [#{word: "one", info: "1info"}, #{word: "two", info: "2info"}, #{word: "three", info: "3info"}]
+ endfunc
+ set omnifunc=Omni_test
+ set completeopt+=longest
END
call writefile(lines, 'Xpreviewscript', 'D')
@@ -809,6 +809,74 @@ func Test_complete_with_longest()
bwipe!
endfunc
+" Test for buffer-local value of 'completeopt'
+func Test_completeopt_buffer_local()
+ set completeopt=menu
+ new
+ call setline(1, ['foofoo', 'foobar', 'foobaz', ''])
+ call assert_equal('', &l:completeopt)
+ call assert_equal('menu', &completeopt)
+ call assert_equal('menu', &g:completeopt)
+
+ setlocal bufhidden=hide
+ enew
+ call setline(1, ['foofoo', 'foobar', 'foobaz', ''])
+ call assert_equal('', &l:completeopt)
+ call assert_equal('menu', &completeopt)
+ call assert_equal('menu', &g:completeopt)
+
+ setlocal completeopt+=fuzzy,noinsert
+ call assert_equal('menu,fuzzy,noinsert', &l:completeopt)
+ call assert_equal('menu,fuzzy,noinsert', &completeopt)
+ call assert_equal('menu', &g:completeopt)
+ call feedkeys("Gccf\<C-X>\<C-N>bz\<C-Y>", 'tnix')
+ call assert_equal('foobaz', getline('.'))
+
+ setlocal completeopt=
+ call assert_equal('', &l:completeopt)
+ call assert_equal('menu', &completeopt)
+ call assert_equal('menu', &g:completeopt)
+ call feedkeys("Gccf\<C-X>\<C-N>\<C-Y>", 'tnix')
+ call assert_equal('foofoo', getline('.'))
+
+ setlocal completeopt+=longest
+ call assert_equal('menu,longest', &l:completeopt)
+ call assert_equal('menu,longest', &completeopt)
+ call assert_equal('menu', &g:completeopt)
+ call feedkeys("Gccf\<C-X>\<C-N>\<C-X>\<C-Z>", 'tnix')
+ call assert_equal('foo', getline('.'))
+
+ setlocal bufhidden=hide
+ buffer #
+ call assert_equal('', &l:completeopt)
+ call assert_equal('menu', &completeopt)
+ call assert_equal('menu', &g:completeopt)
+ call feedkeys("Gccf\<C-X>\<C-N>\<C-Y>", 'tnix')
+ call assert_equal('foofoo', getline('.'))
+
+ setlocal completeopt+=fuzzy,noinsert
+ call assert_equal('menu,fuzzy,noinsert', &l:completeopt)
+ call assert_equal('menu,fuzzy,noinsert', &completeopt)
+ call assert_equal('menu', &g:completeopt)
+ call feedkeys("Gccf\<C-X>\<C-N>bz\<C-Y>", 'tnix')
+ call assert_equal('foobaz', getline('.'))
+
+ buffer #
+ call assert_equal('menu,longest', &l:completeopt)
+ call assert_equal('menu,longest', &completeopt)
+ call assert_equal('menu', &g:completeopt)
+ call feedkeys("Gccf\<C-X>\<C-N>\<C-X>\<C-Z>", 'tnix')
+ call assert_equal('foo', getline('.'))
+
+ setlocal bufhidden=wipe
+ buffer! #
+ bwipe!
+ call assert_equal('', &l:completeopt)
+ call assert_equal('menu', &completeopt)
+ call assert_equal('menu', &g:completeopt)
+
+ set completeopt&
+endfunc
" Test for completing words following a completed word in a line
func Test_complete_wrapscan()
@@ -2451,4 +2519,104 @@ func Test_completefunc_first_call_complete_add()
bwipe!
endfunc
+func Test_complete_fuzzy_match()
+ func OnPumChange()
+ let g:item = get(v:event, 'completed_item', {})
+ let g:word = get(g:item, 'word', v:null)
+ endfunction
+
+ augroup AAAAA_Group
+ au!
+ autocmd CompleteChanged * :call OnPumChange()
+ augroup END
+
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return [#{word: "foo"}, #{word: "foobar"}, #{word: "fooBaz"}, #{word: "foobala"}]
+ endfunc
+
+ new
+ set omnifunc=Omni_test
+ set completeopt+=noinsert,fuzzy
+ call feedkeys("Gi\<C-x>\<C-o>", 'tx')
+ call assert_equal('foo', g:word)
+ call feedkeys("S\<C-x>\<C-o>fb", 'tx')
+ call assert_equal('fooBaz', g:word)
+ call feedkeys("S\<C-x>\<C-o>fa", 'tx')
+ call assert_equal('foobar', g:word)
+ " select next
+ call feedkeys("S\<C-x>\<C-o>fb\<C-n>", 'tx')
+ call assert_equal('foobar', g:word)
+ " can cyclically select next
+ call feedkeys("S\<C-x>\<C-o>fb\<C-n>\<C-n>\<C-n>", 'tx')
+ call assert_equal(v:null, g:word)
+ " select prev
+ call feedkeys("S\<C-x>\<C-o>fb\<C-p>", 'tx')
+ call assert_equal(v:null, g:word)
+ " can cyclically select prev
+ call feedkeys("S\<C-x>\<C-o>fb\<C-p>\<C-p>\<C-p>\<C-p>", 'tx')
+ call assert_equal('fooBaz', g:word)
+
+ func Comp()
+ call complete(col('.'), ["fooBaz", "foobar", "foobala"])
+ return ''
+ endfunc
+ call feedkeys("i\<C-R>=Comp()\<CR>", 'tx')
+ call assert_equal('fooBaz', g:word)
+
+ " respect noselect
+ set completeopt+=noselect
+ call feedkeys("S\<C-x>\<C-o>fb", 'tx')
+ call assert_equal(v:null, g:word)
+ call feedkeys("S\<C-x>\<C-o>fb\<C-n>", 'tx')
+ call assert_equal('fooBaz', g:word)
+
+ " avoid breaking default completion behavior
+ set completeopt=fuzzy,menu
+ call setline(1, ['hello help hero h'])
+ " Use "!" flag of feedkeys() so that ex_normal_busy is not set and
+ " ins_compl_check_keys() is not skipped.
+ " Add a "0" after the <Esc> to avoid waiting for an escape sequence.
+ call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
+ call assert_equal('hello help hero hello', getline('.'))
+ set completeopt+=noinsert
+ call setline(1, ['hello help hero h'])
+ call feedkeys("A\<C-X>\<C-N>\<Esc>0", 'tx!')
+ call assert_equal('hello help hero h', getline('.'))
+
+ " clean up
+ set omnifunc=
+ bw!
+ set complete& completeopt&
+ autocmd! AAAAA_Group
+ augroup! AAAAA_Group
+ delfunc OnPumChange
+ delfunc Omni_test
+ delfunc Comp
+ unlet g:item
+ unlet g:word
+endfunc
+
+" Check that tie breaking is stable for completeopt+=fuzzy (which should
+" behave the same on different platforms).
+func Test_complete_fuzzy_match_tie()
+ new
+ set completeopt+=fuzzy,noselect
+ call setline(1, ['aaabbccc', 'aaabbCCC', 'aaabbcccc', 'aaabbCCCC', ''])
+
+ call feedkeys("Gcc\<C-X>\<C-N>ab\<C-N>\<C-Y>", 'tx')
+ call assert_equal('aaabbccc', getline('.'))
+ call feedkeys("Gcc\<C-X>\<C-N>ab\<C-N>\<C-N>\<C-Y>", 'tx')
+ call assert_equal('aaabbCCC', getline('.'))
+ call feedkeys("Gcc\<C-X>\<C-N>ab\<C-N>\<C-N>\<C-N>\<C-Y>", 'tx')
+ call assert_equal('aaabbcccc', getline('.'))
+ call feedkeys("Gcc\<C-X>\<C-N>ab\<C-N>\<C-N>\<C-N>\<C-N>\<C-Y>", 'tx')
+ call assert_equal('aaabbCCCC', getline('.'))
+
+ bwipe!
+ set completeopt&
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim
index 4c69476..12a6dd4 100644
--- a/src/testdir/test_listdict.vim
+++ b/src/testdir/test_listdict.vim
@@ -60,6 +60,9 @@ func Test_list_slice()
assert_equal([1, 2], l[-3 : -1])
END
call v9.CheckDefAndScriptSuccess(lines)
+
+ call assert_fails('let l[[]] = 1', 'E730: Using a List as a String')
+ call assert_fails('let l[1 : []] = [1]', 'E730: Using a List as a String')
endfunc
" List identity
@@ -178,6 +181,19 @@ func Test_list_assign()
END
call v9.CheckScriptFailure(['vim9script'] + lines, 'E688:')
call v9.CheckDefExecFailure(lines, 'E1093: Expected 2 items but got 1')
+
+ let lines =<< trim END
+ VAR l = [2]
+ LET l += test_null_list()
+ call assert_equal([2], l)
+ LET l = test_null_list()
+ LET l += [1]
+ call assert_equal([1], l)
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
+
+ let d = {'abc': [1, 2, 3]}
+ call assert_fails('let d.abc[0:0z10] = [10, 20]', 'E976: Using a Blob as a String')
endfunc
" test for range assign
@@ -447,6 +463,9 @@ func Test_dict_assign()
n.key = 3
END
call v9.CheckDefFailure(lines, 'E1141:')
+
+ let d = {'abc': {}}
+ call assert_fails("let d.abc[0z10] = 10", 'E976: Using a Blob as a String')
endfunc
" Function in script-local List or Dict
@@ -1505,6 +1524,8 @@ func Test_indexof()
call assert_equal(-1, indexof(test_null_list(), {i, v -> v == 'a'}))
call assert_equal(-1, indexof(l, test_null_string()))
call assert_equal(-1, indexof(l, test_null_function()))
+ call assert_equal(-1, indexof(l, ""))
+ call assert_fails('let i = indexof(l, " ")', 'E15:')
" failure cases
call assert_fails('let i = indexof(l, "v:val == ''cyan''")', 'E735:')
diff --git a/src/testdir/test_method.vim b/src/testdir/test_method.vim
index 120fade..f18ac14 100644
--- a/src/testdir/test_method.vim
+++ b/src/testdir/test_method.vim
@@ -136,6 +136,13 @@ func Test_method_syntax()
call assert_fails('eval [1, 2, 3] ->sort ()', 'E274:')
call assert_fails('eval [1, 2, 3]-> sort ()', 'E274:')
call assert_fails('eval [1, 2, 3]-> sort()', 'E274:')
+
+ " Test for using a method name containing a curly brace name
+ let s = 'len'
+ call assert_equal(4, "xxxx"->str{s}())
+
+ " Test for using a method in an interpolated string
+ call assert_equal('4', $'{"xxxx"->strlen()}')
endfunc
func Test_method_lambda()
diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim
index 4b7e5e6..83594d2 100644
--- a/src/testdir/test_normal.vim
+++ b/src/testdir/test_normal.vim
@@ -402,17 +402,17 @@ func Test_normal08_fold()
" First fold
norm! V4jzf
" check that folds have been created
- call assert_equal(['50/*{{{*/', '51', '52', '53', '54/*}}}*/'], getline(50,54))
+ call assert_equal(['50/* {{{ */', '51', '52', '53', '54/* }}} */'], getline(50,54))
" Second fold
46
norm! V10jzf
" check that folds have been created
- call assert_equal('46/*{{{*/', getline(46))
- call assert_equal('60/*}}}*/', getline(60))
+ call assert_equal('46/* {{{ */', getline(46))
+ call assert_equal('60/* }}} */', getline(60))
norm! k
call assert_equal('45', getline('.'))
norm! j
- call assert_equal('46/*{{{*/', getline('.'))
+ call assert_equal('46/* {{{ */', getline('.'))
norm! j
call assert_equal('61', getline('.'))
norm! k
@@ -421,12 +421,12 @@ func Test_normal08_fold()
norm! k
call assert_equal('45', getline('.'))
norm! j
- call assert_equal('46/*{{{*/', getline('.'))
+ call assert_equal('46/* {{{ */', getline('.'))
norm! j
call assert_equal('47', getline('.'))
norm! k
norm! zcVzO
- call assert_equal('46/*{{{*/', getline('.'))
+ call assert_equal('46/* {{{ */', getline('.'))
norm! j
call assert_equal('47', getline('.'))
norm! j
@@ -434,7 +434,7 @@ func Test_normal08_fold()
norm! j
call assert_equal('49', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
call assert_equal('51', getline('.'))
" delete folds
@@ -1383,14 +1383,14 @@ func Test_normal18_z_fold()
" First fold
norm! 4zF
" check that folds have been created
- call assert_equal(['50/*{{{*/', '51', '52', '53/*}}}*/'], getline(50,53))
+ call assert_equal(['50/* {{{ */', '51', '52', '53/* }}} */'], getline(50,53))
" Test for zd
51
norm! 2zF
call assert_equal(2, foldlevel('.'))
norm! kzd
- call assert_equal(['50', '51/*{{{*/', '52/*}}}*/', '53'], getline(50,53))
+ call assert_equal(['50', '51/* {{{ */', '52/* }}} */', '53'], getline(50,53))
norm! j
call assert_equal(1, foldlevel('.'))
@@ -1409,7 +1409,7 @@ func Test_normal18_z_fold()
norm! 2zF
90
norm! 4zF
- call assert_equal(['85/*{{{*/', '86/*{{{*/', '87/*}}}*/', '88/*}}}*/', '89', '90/*{{{*/', '91', '92', '93/*}}}*/'], getline(85,93))
+ call assert_equal(['85/* {{{ */', '86/* {{{ */', '87/* }}} */', '88/* }}} */', '89', '90/* {{{ */', '91', '92', '93/* }}} */'], getline(85,93))
norm! zE
call assert_equal(['85', '86', '87', '88', '89', '90', '91', '92', '93'], getline(85,93))
@@ -1421,9 +1421,9 @@ func Test_normal18_z_fold()
norm! k
call assert_equal('49', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
- call assert_equal('51/*}}}*/', getline('.'))
+ call assert_equal('51/* }}} */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
call assert_equal(0, &foldenable)
@@ -1433,7 +1433,7 @@ func Test_normal18_z_fold()
norm! zN
call assert_equal('49', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
call assert_equal(1, &foldenable)
@@ -1454,9 +1454,9 @@ func Test_normal18_z_fold()
norm! k
call assert_equal('49', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
- call assert_equal('51/*}}}*/', getline('.'))
+ call assert_equal('51/* }}} */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
50
@@ -1464,7 +1464,7 @@ func Test_normal18_z_fold()
norm! k
call assert_equal('49', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
@@ -1473,14 +1473,14 @@ func Test_normal18_z_fold()
norm! k
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
49
norm! za
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
set nofoldenable
@@ -1494,11 +1494,11 @@ func Test_normal18_z_fold()
norm! 2k
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
- call assert_equal('51/*}}}*/', getline('.'))
+ call assert_equal('51/* }}} */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
@@ -1510,11 +1510,11 @@ func Test_normal18_z_fold()
norm! 2k
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
- call assert_equal('51/*}}}*/', getline('.'))
+ call assert_equal('51/* }}} */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
@@ -1526,7 +1526,7 @@ func Test_normal18_z_fold()
norm! k
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
@@ -1546,7 +1546,7 @@ func Test_normal18_z_fold()
norm! k
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
set nofoldenable
@@ -1555,7 +1555,7 @@ func Test_normal18_z_fold()
norm! k
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
@@ -1565,7 +1565,7 @@ func Test_normal18_z_fold()
norm! zCk
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
@@ -1576,7 +1576,7 @@ func Test_normal18_z_fold()
norm! zx
call assert_equal(1, &foldenable)
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
@@ -1588,17 +1588,17 @@ func Test_normal18_z_fold()
norm! 3k
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
- call assert_equal('51/*}}}*/', getline('.'))
+ call assert_equal('51/* }}} */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
norm! j
call assert_equal('53', getline('.'))
norm! j
- call assert_equal('54/*}}}*/', getline('.'))
+ call assert_equal('54/* }}} */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
@@ -1610,15 +1610,15 @@ func Test_normal18_z_fold()
call assert_equal(1, &foldenable)
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
norm! j
call assert_equal('53', getline('.'))
norm! j
- call assert_equal('54/*}}}*/', getline('.'))
+ call assert_equal('54/* }}} */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
@@ -1631,7 +1631,7 @@ func Test_normal18_z_fold()
norm! k
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
@@ -1648,7 +1648,7 @@ func Test_normal18_z_fold()
norm! k
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
@@ -1667,7 +1667,7 @@ func Test_normal18_z_fold()
call assert_equal(0, &foldlevel)
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
call assert_equal('55', getline('.'))
@@ -1685,11 +1685,11 @@ func Test_normal18_z_fold()
call assert_equal(2, &foldlevel)
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
- call assert_equal('51/*}}}*/', getline('.'))
+ call assert_equal('51/* }}} */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
@@ -1705,24 +1705,24 @@ func Test_normal18_z_fold()
call assert_equal(2, &foldlevel)
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
- call assert_equal('51/*}}}*/', getline('.'))
+ call assert_equal('51/* }}} */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
- call append(50, ['a /*{{{*/', 'b /*}}}*/'])
+ call append(50, ['a /* {{{ */', 'b /* }}} */'])
48
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
- call assert_equal('a /*{{{*/', getline('.'))
+ call assert_equal('a /* {{{ */', getline('.'))
norm! j
- call assert_equal('51/*}}}*/', getline('.'))
+ call assert_equal('51/* }}} */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
48
@@ -1731,15 +1731,15 @@ func Test_normal18_z_fold()
call assert_equal(3, &foldlevel)
call assert_equal('48', getline('.'))
norm! j
- call assert_equal('49/*{{{*/', getline('.'))
+ call assert_equal('49/* {{{ */', getline('.'))
norm! j
- call assert_equal('50/*{{{*/', getline('.'))
+ call assert_equal('50/* {{{ */', getline('.'))
norm! j
- call assert_equal('a /*{{{*/', getline('.'))
+ call assert_equal('a /* {{{ */', getline('.'))
norm! j
- call assert_equal('b /*}}}*/', getline('.'))
+ call assert_equal('b /* }}} */', getline('.'))
norm! j
- call assert_equal('51/*}}}*/', getline('.'))
+ call assert_equal('51/* }}} */', getline('.'))
norm! j
call assert_equal('52', getline('.'))
@@ -4260,4 +4260,22 @@ func Test_page_cursor_topbot()
bwipe!
endfunc
+" Test for Ctrl-D with long line
+func Test_halfpage_longline()
+ 10new
+ call setline(1, ['long'->repeat(1000), 'short'])
+ exe "norm! \<C-D>"
+ call assert_equal(2, line('.'))
+ bwipe!
+endfunc
+
+" Test for Ctrl-E with long line and very narrow window,
+" used to cause an inifite loop
+func Test_scroll_longline_no_loop()
+ 4vnew
+ setl smoothscroll number showbreak=> scrolloff=2
+ call setline(1, repeat(['Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'], 3))
+ exe "normal! \<C-E>"
+ bwipe!
+endfunc
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim
index 4b054b5..b5a58f6 100644
--- a/src/testdir/test_partial.vim
+++ b/src/testdir/test_partial.vim
@@ -406,4 +406,18 @@ func Test_compare_partials()
call assert_false(F1 is N1)
endfunc
+func Test_partial_method()
+ func Foo(x, y, z)
+ return x + y + z
+ endfunc
+ let d = {"Fn": function('Foo', [10, 20])}
+ call assert_fails('echo 30->d.Fn()', 'E1265: Cannot use a partial here')
+ delfunc Foo
+endfunc
+
+func Test_non_callable_type_as_method()
+ let d = {"Fn": 10}
+ call assert_fails('echo 30->d.Fn()', 'E1085: Not a callable type: d.Fn')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim
index f5cb8b2..dd01a57 100644
--- a/src/testdir/test_popup.vim
+++ b/src/testdir/test_popup.vim
@@ -908,6 +908,13 @@ func Test_popup_command_dump()
call term_sendkeys(buf, "\<Esc>")
+ if has('rightleft')
+ call term_sendkeys(buf, ":set rightleft\<CR>")
+ call term_sendkeys(buf, "/X\<CR>:popup PopUp\<CR>")
+ call VerifyScreenDump(buf, 'Test_popup_command_rl', {})
+ call term_sendkeys(buf, "\<Esc>:set norightleft\<CR>")
+ endif
+
" Set a timer to change a menu entry while it's displayed. The text should
" not change but the command does. Making the screendump also verifies that
" "changed" shows up, which means the timer triggered.
@@ -930,6 +937,37 @@ func Test_popup_command_dump()
call StopVimInTerminal(buf)
endfunc
+" Test position of right-click menu when clicking near window edge.
+func Test_mouse_popup_position()
+ CheckFeature menu
+ CheckScreendump
+
+ let script =<< trim END
+ set mousemodel=popup_setpos
+ source $VIMRUNTIME/menu.vim
+ call setline(1, join(range(20)))
+ func Trigger(col)
+ call test_setmouse(1, a:col)
+ call feedkeys("\<RightMouse>", 't')
+ endfunc
+ END
+ call writefile(script, 'XmousePopupPosition', 'D')
+ let buf = RunVimInTerminal('-S XmousePopupPosition', #{rows: 20, cols: 50})
+
+ call term_sendkeys(buf, ":call Trigger(45)\<CR>")
+ call VerifyScreenDump(buf, 'Test_mouse_popup_position_01', {})
+ call term_sendkeys(buf, "\<Esc>")
+
+ if has('rightleft')
+ call term_sendkeys(buf, ":set rightleft\<CR>")
+ call term_sendkeys(buf, ":call Trigger(50 + 1 - 45)\<CR>")
+ call VerifyScreenDump(buf, 'Test_mouse_popup_position_02', {})
+ call term_sendkeys(buf, "\<Esc>:set norightleft\<CR>")
+ endif
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_popup_complete_backwards()
new
call setline(1, ['Post', 'Port', 'Po'])
@@ -1175,6 +1213,8 @@ func Test_CompleteChanged()
set completeopt=menu,menuone
call feedkeys("i\<C-X>\<C-O>\<BS>\<BS>\<BS>f", 'tx')
call assert_equal('five', g:word)
+ call feedkeys("i\<C-X>\<C-O>\<BS>\<BS>\<BS>f\<BS>", 'tx')
+ call assert_equal('one', g:word)
autocmd! AAAAA_Group
set complete& completeopt&
@@ -1339,4 +1379,113 @@ func Test_pum_highlights_custom()
call StopVimInTerminal(buf)
endfunc
+" Test match relate highlight group in pmenu
+func Test_pum_highlights_match()
+ CheckScreendump
+ let lines =<< trim END
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return {
+ \ 'words': [
+ \ { 'word': 'foo', 'kind': 'fookind' },
+ \ { 'word': 'foofoo', 'kind': 'fookind' },
+ \ { 'word': 'foobar', 'kind': 'fookind' },
+ \ { 'word': 'fooBaz', 'kind': 'fookind' },
+ \ { 'word': 'foobala', 'kind': 'fookind' },
+ \ { 'word': '你好' },
+ \ { 'word': '你好吗' },
+ \ { 'word': '你不好吗' },
+ \ { 'word': '你可好吗' },
+ \]}
+ endfunc
+
+ func Comp()
+ let col = col('.')
+ if getline('.') == 'f'
+ let col -= 1
+ endif
+ call complete(col, [
+ \ #{word: "foo", icase: 1},
+ \ #{word: "Foobar", icase: 1},
+ \ #{word: "fooBaz", icase: 1},
+ \])
+ return ''
+ endfunc
+
+ set omnifunc=Omni_test
+ set completeopt=menu,noinsert,fuzzy
+ hi PmenuMatchSel ctermfg=6 ctermbg=7
+ hi PmenuMatch ctermfg=4 ctermbg=225
+ END
+ call writefile(lines, 'Xscript', 'D')
+ let buf = RunVimInTerminal('-S Xscript', {})
+ call TermWait(buf)
+ call term_sendkeys(buf, "i\<C-X>\<C-O>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "fo")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_pum_highlights_03', {})
+ call term_sendkeys(buf, "\<Esc>S\<C-X>\<C-O>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "你")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_pum_highlights_04', {})
+ call term_sendkeys(buf, "吗")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_pum_highlights_05', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ if has('rightleft')
+ call term_sendkeys(buf, ":set rightleft\<CR>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "S\<C-X>\<C-O>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "fo")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_pum_highlights_06', {})
+ call term_sendkeys(buf, "\<Esc>S\<C-X>\<C-O>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "你")
+ call VerifyScreenDump(buf, 'Test_pum_highlights_06a', {})
+ call term_sendkeys(buf, "吗")
+ call VerifyScreenDump(buf, 'Test_pum_highlights_06b', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+ call term_sendkeys(buf, ":set norightleft\<CR>")
+ call TermWait(buf)
+ endif
+
+ call term_sendkeys(buf, ":set completeopt-=fuzzy\<CR>")
+ call TermWait(buf)
+ call term_sendkeys(buf, "S\<C-X>\<C-O>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "fo")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_pum_highlights_07', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+ if has('rightleft')
+ call term_sendkeys(buf, ":set rightleft\<CR>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "S\<C-X>\<C-O>")
+ call TermWait(buf, 50)
+ call term_sendkeys(buf, "fo")
+ call TermWait(buf, 50)
+ call VerifyScreenDump(buf, 'Test_pum_highlights_08', {})
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+ call term_sendkeys(buf, ":set norightleft\<CR>")
+ endif
+
+ call term_sendkeys(buf, "S\<C-R>=Comp()\<CR>f")
+ call VerifyScreenDump(buf, 'Test_pum_highlights_09', {})
+ call term_sendkeys(buf, "o\<BS>\<C-R>=Comp()\<CR>")
+ call VerifyScreenDump(buf, 'Test_pum_highlights_09', {})
+
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+ call TermWait(buf)
+
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim
index c784a2d..0b61815 100644
--- a/src/testdir/test_quickfix.vim
+++ b/src/testdir/test_quickfix.vim
@@ -6496,4 +6496,74 @@ func Test_efm_format_b()
call setqflist([], 'f')
endfunc
+func XbufferTests_range(cchar)
+ call s:setup_commands(a:cchar)
+
+ enew!
+ let lines =<< trim END
+ Xtestfile7:700:10:Line 700
+ Xtestfile8:800:15:Line 800
+ END
+ silent! call setline(1, lines)
+ norm! Vy
+ " Note: We cannot use :Xbuffer here,
+ " it doesn't properly fail, so we need to
+ " test using the raw c/l commands.
+ " (also further down)
+ if (a:cchar == 'c')
+ exe "'<,'>cbuffer!"
+ else
+ exe "'<,'>lbuffer!"
+ endif
+ let l = g:Xgetlist()
+ call assert_true(len(l) == 1 &&
+ \ l[0].lnum == 700 && l[0].col == 10 && l[0].text ==# 'Line 700')
+
+ enew!
+ let lines =<< trim END
+ Xtestfile9:900:55:Line 900
+ Xtestfile10:950:66:Line 950
+ END
+ silent! call setline(1, lines)
+ if (a:cchar == 'c')
+ 1cgetbuffer
+ else
+ 1lgetbuffer
+ endif
+ let l = g:Xgetlist()
+ call assert_true(len(l) == 1 &&
+ \ l[0].lnum == 900 && l[0].col == 55 && l[0].text ==# 'Line 900')
+
+ enew!
+ let lines =<< trim END
+ Xtestfile11:700:20:Line 700
+ Xtestfile12:750:25:Line 750
+ END
+ silent! call setline(1, lines)
+ if (a:cchar == 'c')
+ 1,1caddbuffer
+ else
+ 1,1laddbuffer
+ endif
+ let l = g:Xgetlist()
+ call assert_true(len(l) == 2 &&
+ \ l[0].lnum == 900 && l[0].col == 55 && l[0].text ==# 'Line 900' &&
+ \ l[1].lnum == 700 && l[1].col == 20 && l[1].text ==# 'Line 700')
+ enew!
+
+ " Check for invalid range
+ " Using Xbuffer will not run the range check in the cbuffer/lbuffer
+ " commands. So directly call the commands.
+ if (a:cchar == 'c')
+ call assert_fails('900,999caddbuffer', 'E16:')
+ else
+ call assert_fails('900,999laddbuffer', 'E16:')
+ endif
+endfunc
+
+func Test_cbuffer_range()
+ call XbufferTests_range('c')
+ call XbufferTests_range('l')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_remote.vim b/src/testdir/test_remote.vim
index 1475052..fd8b0af 100644
--- a/src/testdir/test_remote.vim
+++ b/src/testdir/test_remote.vim
@@ -12,7 +12,7 @@ source term_util.vim
let s:remote_works = 0
let s:skip = 'Skipped: --remote feature is not possible'
-" nees to be run as first test to verify, that vim --servername works
+" needs to be run as first test to verify, that vim --servername works
func Verify_remote_feature_works()
CheckRunVimInTerminal
enew
diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim
index cda9bf0..1b60019 100644
--- a/src/testdir/test_scroll_opt.vim
+++ b/src/testdir/test_scroll_opt.vim
@@ -961,6 +961,51 @@ func Test_smoothscroll_insert_bottom()
call StopVimInTerminal(buf)
endfunc
+func Test_smoothscroll_in_qf_window()
+ CheckFeature quickfix
+ CheckScreendump
+
+ let lines =<< trim END
+ set nocompatible display=lastline
+ copen 5
+ setlocal number smoothscroll
+ let g:l = [{'text': 'foo'}] + repeat([{'text': join(range(30))}], 10)
+ call setqflist(g:l, 'r')
+ normal! G
+ wincmd t
+ let g:l1 = [{'text': join(range(1000))}]
+ END
+ call writefile(lines, 'XSmoothScrollInQfWindow', 'D')
+ let buf = RunVimInTerminal('-u NONE -S XSmoothScrollInQfWindow', #{rows: 20, cols: 60})
+ call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_1', {})
+
+ call term_sendkeys(buf, ":call setqflist([], 'r')\<CR>")
+ call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_2', {})
+
+ call term_sendkeys(buf, ":call setqflist(g:l, 'r')\<CR>")
+ call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_3', {})
+
+ call term_sendkeys(buf, ":call setqflist(g:l1, 'r')\<CR>")
+ call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_4', {})
+
+ call term_sendkeys(buf, "\<C-W>b$\<C-W>t")
+ call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_5', {})
+
+ call term_sendkeys(buf, ":call setqflist([], 'r')\<CR>")
+ call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_2', {})
+
+ call term_sendkeys(buf, ":call setqflist(g:l1, 'r')\<CR>")
+ call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_4', {})
+
+ call term_sendkeys(buf, "\<C-W>b$\<C-W>t")
+ call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_5', {})
+
+ call term_sendkeys(buf, ":call setqflist(g:l, 'r')\<CR>")
+ call VerifyScreenDump(buf, 'Test_smoothscroll_in_qf_window_3', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_smoothscroll_in_zero_width_window()
set cpo+=n number smoothscroll
set winwidth=99999 winminwidth=0
@@ -1108,4 +1153,39 @@ func Test_smoothscroll_long_line_zb()
bwipe!
endfunc
+func Test_smooth_long_scrolloff()
+ CheckScreendump
+
+ let lines =<< trim END
+ set smoothscroll scrolloff=3
+ call setline(1, ['one', 'two long '->repeat(100), 'three', 'four', 'five', 'six'])
+ END
+ call writefile(lines, 'XSmoothLongScrolloff', 'D')
+ let buf = RunVimInTerminal('-u NONE -S XSmoothLongScrolloff', #{rows: 8, cols: 40})
+ "FIXME: empty screen due to reset_skipcol()/curs_columns() shenanigans
+ call term_sendkeys(buf, ":norm j721|\<CR>")
+ call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_1', {})
+
+ call term_sendkeys(buf, "gj")
+ call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_2', {})
+
+ call term_sendkeys(buf, "gj")
+ call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_3', {})
+
+ call term_sendkeys(buf, "gj")
+ call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_4', {})
+
+ call term_sendkeys(buf, "gj")
+ call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_5', {})
+
+ call term_sendkeys(buf, "gj")
+ call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_6', {})
+
+ call term_sendkeys(buf, "gk")
+ "FIXME: empty screen due to reset_skipcol()/curs_columns() shenanigans
+ call VerifyScreenDump(buf, 'Test_smooth_long_scrolloff_7', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_shell.vim b/src/testdir/test_shell.vim
index 7d91dff..2ac5596 100644
--- a/src/testdir/test_shell.vim
+++ b/src/testdir/test_shell.vim
@@ -158,6 +158,10 @@ func Test_shellescape()
call assert_equal("'te\\#xt'", shellescape("te#xt", 1))
call assert_equal("'te!xt'", shellescape("te!xt"))
call assert_equal("'te\\!xt'", shellescape("te!xt", 1))
+ call assert_equal("'te<cword>xt'", shellescape("te<cword>xt"))
+ call assert_equal("'te\\<cword>xt'", shellescape("te<cword>xt", 1))
+ call assert_equal("'te<cword>%xt'", shellescape("te<cword>%xt"))
+ call assert_equal("'te\\<cword>\\%xt'", shellescape("te<cword>%xt", 1))
call assert_equal("'te\nxt'", shellescape("te\nxt"))
call assert_equal("'te\\\nxt'", shellescape("te\nxt", 1))
diff --git a/src/testdir/test_shortpathname.vim b/src/testdir/test_shortpathname.vim
index 5964630..0c41692 100644
--- a/src/testdir/test_shortpathname.vim
+++ b/src/testdir/test_shortpathname.vim
@@ -16,18 +16,14 @@ func TestIt(file, bits, expected)
endif
endfunc
-func Test_ColonEight()
- let save_dir = getcwd()
-
- " This could change for CygWin to //cygdrive/c .
- let dir1 = 'c:/x.x.y'
+func s:SetupDir(dir)
let trycount = 5
while 1
- if !filereadable(dir1) && !isdirectory(dir1)
+ if !filereadable(a:dir) && !isdirectory(a:dir)
break
endif
if trycount == 1
- call assert_report("Fatal: '" . dir1 . "' exists, cannot run this test")
+ call assert_report("Fatal: '" . a:dir . "' exists, cannot run this test")
return
endif
" When tests run in parallel the directory may exist, wait a bit until it
@@ -35,6 +31,15 @@ func Test_ColonEight()
sleep 5
let trycount -= 1
endwhile
+endfunc
+
+
+func Test_ColonEight()
+ let save_dir = getcwd()
+
+ " This could change for CygWin to //cygdrive/c .
+ let dir1 = 'c:/x.x.y'
+ call s:SetupDir(dir1)
let file1 = dir1 . '/zz.y.txt'
let nofile1 = dir1 . '/z.y.txt'
@@ -78,7 +83,8 @@ func Test_ColonEight()
endfunc
func Test_ColonEight_MultiByte()
- let dir = 'Xtest'
+ let dir = 'c:/Xtest_C8MB'
+ call s:SetupDir(dir)
let file = dir . '/日本語のファイル.txt'
diff --git a/src/testdir/test_spellrare.vim b/src/testdir/test_spellrare.vim
new file mode 100644
index 0000000..ceb35cb
--- /dev/null
+++ b/src/testdir/test_spellrare.vim
@@ -0,0 +1,61 @@
+" Test spell checking
+
+source check.vim
+CheckFeature spell
+
+" Test spellbadword() with argument, specifically to move to "rare" words
+" in normal mode.
+func Test_spellrareword()
+ set spell
+
+ " Create a small word list to test that spellbadword('...')
+ " can return ['...', 'rare'].
+ let lines =<< trim END
+ foo
+ foobar/?
+ foobara/?
+ END
+ call writefile(lines, 'Xwords', 'D')
+
+ mkspell! Xwords.spl Xwords
+ set spelllang=Xwords.spl
+ call assert_equal(['foobar', 'rare'], spellbadword('foo foobar'))
+
+ new
+ call setline(1, ['foo', '', 'foo bar foo bar foobara foo foo foo foobar', '', 'End'])
+ set spell wrapscan
+ normal ]s
+ call assert_equal('foo', expand('<cword>'))
+ normal ]s
+ call assert_equal('bar', expand('<cword>'))
+
+ normal ]r
+ call assert_equal('foobara', expand('<cword>'))
+ normal ]r
+ call assert_equal('foobar', expand('<cword>'))
+ normal ]r
+ call assert_equal('foobara', expand('<cword>'))
+ normal 2]r
+ call assert_equal('foobara', expand('<cword>'))
+
+ normal [r
+ call assert_equal('foobar', expand('<cword>'))
+ normal [r
+ call assert_equal('foobara', expand('<cword>'))
+ normal [r
+ call assert_equal('foobar', expand('<cword>'))
+ normal 2[r
+ call assert_equal('foobar', expand('<cword>'))
+
+ bwipe!
+ set nospell
+
+ call delete('Xwords.spl')
+ set spelllang&
+ set spell&
+
+ " set 'encoding' to clear the word list
+ set encoding=utf-8
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_substitute.vim b/src/testdir/test_substitute.vim
index 7c2bbb4..afdc104 100644
--- a/src/testdir/test_substitute.vim
+++ b/src/testdir/test_substitute.vim
@@ -173,6 +173,16 @@ func Test_substitute_repeat()
call feedkeys("Qsc\<CR>y", 'tx')
bwipe!
endfunc
+
+" Test :s with ? as delimiter.
+func Test_substitute_question_delimiter()
+ new
+ call setline(1, '??:??')
+ %s?\?\??!!?g
+ call assert_equal('!!:!!', getline(1))
+ bwipe!
+endfunc
+
" Test %s/\n// which is implemented as a special case to use a
" more efficient join rather than doing a regular substitution.
func Test_substitute_join()
@@ -1498,4 +1508,18 @@ func Test_substitute_expr_recursive()
exe bufnr .. "bw!"
endfunc
+" Test for changing 'cpo' in a substitute expression
+func Test_substitute_expr_cpo()
+ func XSubExpr()
+ set cpo=
+ return 'x'
+ endfunc
+
+ let save_cpo = &cpo
+ call assert_equal('xxx', substitute('abc', '.', '\=XSubExpr()', 'g'))
+ call assert_equal(save_cpo, &cpo)
+
+ delfunc XSubExpr
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim
index 2abf1f6..432906e 100644
--- a/src/testdir/test_tagjump.vim
+++ b/src/testdir/test_tagjump.vim
@@ -1560,4 +1560,22 @@ func Test_tagbsearch()
set tags& tagbsearch&
endfunc
+" Test tag guessing with very short names
+func Test_tag_guess_short()
+ call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
+ \ "y\tXf\t/^y()/"],
+ \ 'Xt', 'D')
+ set tags=Xt cpoptions+=t
+ call writefile(['', 'int * y () {}', ''], 'Xf', 'D')
+
+ let v:statusmsg = ''
+ let @/ = ''
+ ta y
+ call assert_match('E435:', v:statusmsg)
+ call assert_equal(2, line('.'))
+ call assert_match('<y', @/)
+
+ set tags& cpoptions-=t
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_termdebug.vim b/src/testdir/test_termdebug.vim
index 68bffd4..a9762df 100644
--- a/src/testdir/test_termdebug.vim
+++ b/src/testdir/test_termdebug.vim
@@ -230,26 +230,26 @@ endfunc
func Test_termdebug_mapping()
%bw!
- call assert_equal(maparg('K', 'n', 0, 1)->empty(), 1)
- call assert_equal(maparg('-', 'n', 0, 1)->empty(), 1)
- call assert_equal(maparg('+', 'n', 0, 1)->empty(), 1)
+ call assert_true(maparg('K', 'n', 0, 1)->empty())
+ call assert_true(maparg('-', 'n', 0, 1)->empty())
+ call assert_true(maparg('+', 'n', 0, 1)->empty())
Termdebug
call WaitForAssert({-> assert_equal(3, winnr('$'))})
wincmd b
- call assert_equal(maparg('K', 'n', 0, 1)->empty(), 0)
- call assert_equal(maparg('-', 'n', 0, 1)->empty(), 0)
- call assert_equal(maparg('+', 'n', 0, 1)->empty(), 0)
- call assert_equal(maparg('K', 'n', 0, 1).buffer, 0)
- call assert_equal(maparg('-', 'n', 0, 1).buffer, 0)
- call assert_equal(maparg('+', 'n', 0, 1).buffer, 0)
- call assert_equal(maparg('K', 'n', 0, 1).rhs, ':Evaluate<CR>')
+ call assert_false(maparg('K', 'n', 0, 1)->empty())
+ call assert_false(maparg('-', 'n', 0, 1)->empty())
+ call assert_false(maparg('+', 'n', 0, 1)->empty())
+ call assert_false(maparg('K', 'n', 0, 1).buffer)
+ call assert_false(maparg('-', 'n', 0, 1).buffer)
+ call assert_false(maparg('+', 'n', 0, 1).buffer)
+ call assert_equal(':Evaluate<CR>', maparg('K', 'n', 0, 1).rhs)
wincmd t
quit!
redraw!
call WaitForAssert({-> assert_equal(1, winnr('$'))})
- call assert_equal(maparg('K', 'n', 0, 1)->empty(), 1)
- call assert_equal(maparg('-', 'n', 0, 1)->empty(), 1)
- call assert_equal(maparg('+', 'n', 0, 1)->empty(), 1)
+ call assert_true(maparg('K', 'n', 0, 1)->empty())
+ call assert_true(maparg('-', 'n', 0, 1)->empty())
+ call assert_true(maparg('+', 'n', 0, 1)->empty())
%bw!
nnoremap K :echom "K"<cr>
@@ -258,24 +258,24 @@ func Test_termdebug_mapping()
Termdebug
call WaitForAssert({-> assert_equal(3, winnr('$'))})
wincmd b
- call assert_equal(maparg('K', 'n', 0, 1)->empty(), 0)
- call assert_equal(maparg('-', 'n', 0, 1)->empty(), 0)
- call assert_equal(maparg('+', 'n', 0, 1)->empty(), 0)
- call assert_equal(maparg('K', 'n', 0, 1).buffer, 0)
- call assert_equal(maparg('-', 'n', 0, 1).buffer, 0)
- call assert_equal(maparg('+', 'n', 0, 1).buffer, 0)
- call assert_equal(maparg('K', 'n', 0, 1).rhs, ':Evaluate<CR>')
+ call assert_false(maparg('K', 'n', 0, 1)->empty())
+ call assert_false(maparg('-', 'n', 0, 1)->empty())
+ call assert_false(maparg('+', 'n', 0, 1)->empty())
+ call assert_false(maparg('K', 'n', 0, 1).buffer)
+ call assert_false(maparg('-', 'n', 0, 1).buffer)
+ call assert_false(maparg('+', 'n', 0, 1).buffer)
+ call assert_equal(':Evaluate<CR>', maparg('K', 'n', 0, 1).rhs)
wincmd t
quit!
redraw!
call WaitForAssert({-> assert_equal(1, winnr('$'))})
- call assert_equal(maparg('K', 'n', 0, 1)->empty(), 0)
- call assert_equal(maparg('-', 'n', 0, 1)->empty(), 0)
- call assert_equal(maparg('+', 'n', 0, 1)->empty(), 0)
- call assert_equal(maparg('K', 'n', 0, 1).buffer, 0)
- call assert_equal(maparg('-', 'n', 0, 1).buffer, 0)
- call assert_equal(maparg('+', 'n', 0, 1).buffer, 0)
- call assert_equal(maparg('K', 'n', 0, 1).rhs, ':echom "K"<cr>')
+ call assert_false(maparg('K', 'n', 0, 1)->empty())
+ call assert_false(maparg('-', 'n', 0, 1)->empty())
+ call assert_false(maparg('+', 'n', 0, 1)->empty())
+ call assert_false(maparg('K', 'n', 0, 1).buffer)
+ call assert_false(maparg('-', 'n', 0, 1).buffer)
+ call assert_false(maparg('+', 'n', 0, 1).buffer)
+ call assert_equal(':echom "K"<cr>', maparg('K', 'n', 0, 1).rhs)
%bw!
nnoremap <buffer> K :echom "bK"<cr>
@@ -284,20 +284,70 @@ func Test_termdebug_mapping()
Termdebug
call WaitForAssert({-> assert_equal(3, winnr('$'))})
wincmd b
- call assert_equal(maparg('K', 'n', 0, 1).buffer, 1)
- call assert_equal(maparg('-', 'n', 0, 1).buffer, 1)
- call assert_equal(maparg('+', 'n', 0, 1).buffer, 1)
+ call assert_true(maparg('K', 'n', 0, 1).buffer)
+ call assert_true(maparg('-', 'n', 0, 1).buffer)
+ call assert_true(maparg('+', 'n', 0, 1).buffer)
call assert_equal(maparg('K', 'n', 0, 1).rhs, ':echom "bK"<cr>')
wincmd t
quit!
redraw!
call WaitForAssert({-> assert_equal(1, winnr('$'))})
- call assert_equal(maparg('K', 'n', 0, 1).buffer, 1)
- call assert_equal(maparg('-', 'n', 0, 1).buffer, 1)
- call assert_equal(maparg('+', 'n', 0, 1).buffer, 1)
- call assert_equal(maparg('K', 'n', 0, 1).rhs, ':echom "bK"<cr>')
+ call assert_true(maparg('K', 'n', 0, 1).buffer)
+ call assert_true(maparg('-', 'n', 0, 1).buffer)
+ call assert_true(maparg('+', 'n', 0, 1).buffer)
+ call assert_equal(':echom "bK"<cr>', maparg('K', 'n', 0, 1).rhs)
%bw!
endfunc
+func Test_termdebug_bufnames()
+ " Test if user has filename/folders named gdb, Termdebug-gdb-console,
+ " etc. in the current directory
+ let g:termdebug_config = {}
+ let g:termdebug_config['use_prompt'] = 1
+ let filename = 'gdb'
+ let replacement_filename = 'Termdebug-gdb-console'
+
+ call writefile(['This', 'is', 'a', 'test'], filename, 'D')
+ " Throw away the file once the test has done.
+ Termdebug
+ " Once termdebug has completed the startup you should have 3 windows on screen
+ call WaitForAssert({-> assert_equal(3, winnr('$'))})
+ " A file named filename already exists in the working directory,
+ " hence you must call the newly created buffer differently
+ call WaitForAssert({-> assert_false(bufexists(filename))})
+ call WaitForAssert({-> assert_true(bufexists(replacement_filename))})
+ quit!
+ call WaitForAssert({-> assert_equal(1, winnr('$'))})
+
+ " Check if error message is in :message
+ let g:termdebug_config['disasm_window'] = 1
+ let filename = 'Termdebug-asm-listing'
+ call writefile(['This', 'is', 'a', 'test'], filename, 'D')
+ " Check only the head of the error message
+ let error_message = "You have a file/folder named '" .. filename .. "'"
+ Termdebug
+ " Once termdebug has completed the startup you should have 4 windows on screen
+ call WaitForAssert({-> assert_equal(4, winnr('$'))})
+ call WaitForAssert({-> assert_notequal(-1, stridx(execute('messages'), error_message))})
+ quit!
+ wincmd b
+ wincmd q
+ call WaitForAssert({-> assert_equal(1, winnr('$'))})
+
+ unlet g:termdebug_config
+endfunc
+
+function Test_termdebug_save_restore_variables()
+ let &mousemodel=''
+ Termdebug
+ call WaitForAssert({-> assert_equal(3, winnr('$'))})
+ call WaitForAssert({-> assert_match(&mousemodel, 'popup_setpos')})
+ wincmd t
+ quit!
+ call WaitForAssert({-> assert_equal(1, winnr('$'))})
+ call WaitForAssert({-> assert_true(empty(&mousemodel))})
+endfunction
+
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_terminal3.vim b/src/testdir/test_terminal3.vim
index 223bcc5..3cca1b0 100644
--- a/src/testdir/test_terminal3.vim
+++ b/src/testdir/test_terminal3.vim
@@ -10,6 +10,8 @@ source screendump.vim
source mouse.vim
source term_util.vim
+import './vim9.vim' as v9
+
let $PROMPT_COMMAND=''
func Test_terminal_altscreen()
@@ -935,7 +937,14 @@ func Test_terminal_vt420()
CheckRunVimInTerminal
" For Termcap
CheckUnix
- let rows=15
+ CheckExecutable infocmp
+ let a = system('infocmp vt420')
+ if v:shell_error
+ " reset v:shell_error
+ let a = system('true')
+ throw 'Skipped: vt420 terminfo not available'
+ endif
+ let rows = 15
call writefile([':set term=vt420'], 'Xterm420', 'D')
let buf = RunVimInTerminal('-S Xterm420', #{rows: rows})
@@ -952,4 +961,18 @@ func Test_terminal_vt420()
call StopVimInTerminal(buf)
endfunc
+" Test for using 'vertical' with term_start(). If a following term_start(),
+" doesn't have the 'vertical' attribute, then it should be split horizontally.
+func Test_terminal_vertical()
+ let lines =<< trim END
+ call term_start("NONE", {'vertical': 1})
+ call term_start("NONE")
+ VAR layout = winlayout()
+ call assert_equal('row', layout[0], string(layout))
+ call assert_equal('col', layout[1][0][0], string(layout))
+ :%bw!
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_textformat.vim b/src/testdir/test_textformat.vim
index 5d58486..e31ea22 100644
--- a/src/testdir/test_textformat.vim
+++ b/src/testdir/test_textformat.vim
@@ -1304,7 +1304,7 @@ func Test_correct_cursor_position()
endfunc
" This was crashing Vim
-func Test_textwdith_overflow()
+func Test_textwidth_overflow()
new
setl tw=999999999
normal 10ig
diff --git a/src/testdir/test_vartabs.vim b/src/testdir/test_vartabs.vim
index e15a072..bae00dd 100644
--- a/src/testdir/test_vartabs.vim
+++ b/src/testdir/test_vartabs.vim
@@ -454,5 +454,64 @@ func Test_vartabstop_latin1()
set nocompatible linebreak& list& revins& smarttab& vartabstop&
endfunc
+" Verify that right-shifting and left-shifting adjust lines to the proper
+" tabstops.
+func Test_vartabstop_shift_right_left()
+ new
+ set expandtab
+ set shiftwidth=0
+ set vartabstop=17,11,7
+ exe "norm! aword"
+ let expect = "word"
+ call assert_equal(expect, getline(1))
+
+ " Shift to first tabstop.
+ norm! >>
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift to second tabstop.
+ norm! >>
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift to third tabstop.
+ norm! >>
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift to fourth tabstop, repeating the third shift width.
+ norm! >>
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift back to the third tabstop.
+ norm! <<
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift back to the second tabstop.
+ norm! <<
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift back to the first tabstop.
+ norm! <<
+ let expect = " word"
+ call assert_equal(expect, getline(1))
+
+ " Shift back to the left margin.
+ norm! <<
+ let expect = "word"
+ call assert_equal(expect, getline(1))
+
+ " Shift again back to the left margin.
+ norm! <<
+ let expect = "word"
+ call assert_equal(expect, getline(1))
+
+ bwipeout!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim
index 4296c37..bb08943 100644
--- a/src/testdir/test_vim9_assign.vim
+++ b/src/testdir/test_vim9_assign.vim
@@ -650,7 +650,7 @@ def Test_assign_index()
var bl = 0z11
bl[1] = g:val
END
- v9.CheckDefExecAndScriptFailure(lines, 'E1030: Using a String as a Number: "22"')
+ v9.CheckDefExecAndScriptFailure(lines, ['E1030: Using a String as a Number: "22"', 'E1012: Type mismatch; expected number but got string'])
# should not read the next line when generating "a.b"
var a = {}
@@ -3687,4 +3687,16 @@ def Test_final_var_with_blob_value()
v9.CheckScriptSuccess(lines)
enddef
+" Test for overwriting a script-local function using the s: dictionary
+def Test_override_script_local_func()
+ var lines =<< trim END
+ vim9script
+ def MyFunc()
+ enddef
+ var d: dict<any> = s:
+ d.MyFunc = function('min')
+ END
+ v9.CheckScriptFailure(lines, 'E705: Variable name conflicts with existing function: MyFunc', 5)
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim
index 42d09fe..6b1a50d 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -960,6 +960,8 @@ def Test_execute()
assert_equal("\nhello", res)
res = execute(["echo 'here'", "echo 'there'"])
assert_equal("\nhere\nthere", res)
+ res = execute("echo 'hi'\n# foo")
+ assert_equal("\nhi", res)
v9.CheckSourceDefAndScriptFailure(['execute(123)'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1222: String or List required for argument 1'])
v9.CheckSourceDefFailure(['execute([123])'], 'E1013: Argument 1: type mismatch, expected list<string> but got list<number>')
@@ -2195,6 +2197,7 @@ def Test_indexof()
indexof(l, (i, v) => v.color == 'blue')->assert_equal(1)
indexof(l, (i, v) => v.color == 'blue', {startidx: 1})->assert_equal(1)
indexof(l, (i, v) => v.color == 'blue', {startidx: 2})->assert_equal(3)
+ indexof(l, "")->assert_equal(-1)
var b = 0zdeadbeef
indexof(b, "v:val == 0xef")->assert_equal(3)
@@ -4554,6 +4557,7 @@ enddef
def Test_term_getjob()
CheckRunVimInTerminal
v9.CheckSourceDefAndScriptFailure(['term_getjob(0z10)'], ['E1013: Argument 1: type mismatch, expected string but got blob', 'E1220: String or Number required for argument 1'])
+ v9.CheckSourceDefAndScriptSuccess(['assert_true(term_getjob(0) == null_job)'])
enddef
def Test_term_getline()
@@ -4783,8 +4787,6 @@ def Test_typename()
if has('channel')
assert_equal('channel', test_null_channel()->typename())
endif
- assert_equal('class<Unknown>', typename(null_class))
- assert_equal('object<Unknown>', typename(null_object))
var l: list<func(list<number>): number> = [function('min')]
assert_equal('list<func(list<number>): number>', typename(l))
enddef
@@ -5183,10 +5185,26 @@ enddef
def Test_getregion()
assert_equal(['x'], getregion(getpos('.'), getpos('.'))->map((_, _) => 'x'))
-
- v9.CheckSourceDefAndScriptFailure(['getregion(10, getpos("."))'], ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1211: List required for argument 1'])
- assert_equal([''], getregion(getpos('.'), getpos('.')))
+ assert_equal(['x'], getregionpos(getpos('.'), getpos('.'))->map((_, _) => 'x'))
+
+ v9.CheckSourceDefAndScriptFailure(
+ ['getregion(10, getpos("."))'],
+ ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1211: List required for argument 1']
+ )
+ v9.CheckSourceDefAndScriptFailure(
+ ['getregionpos(10, getpos("."))'],
+ ['E1013: Argument 1: type mismatch, expected list<any> but got number', 'E1211: List required for argument 1']
+ )
+ assert_equal(
+ [''],
+ getregion(getpos('.'), getpos('.'))
+ )
+ assert_equal(
+ [[[bufnr('%'), 1, 0, 0], [bufnr('%'), 1, 0, 0]]],
+ getregionpos(getpos('.'), getpos('.'))
+ )
v9.CheckSourceDefExecFailure(['getregion(getpos("a"), getpos("."))'], 'E1209:')
+ v9.CheckSourceDefExecFailure(['getregionpos(getpos("a"), getpos("."))'], 'E1209:')
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index bd06c6e..e92fcc5 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -103,6 +103,18 @@ def Test_class_basic()
END
v9.CheckSourceFailure(lines, "E488: Trailing characters: | var y = 10", 3)
+ # Comments are allowed after an inline block
+ lines =<< trim END
+ vim9script
+ class Foo
+ static const bar = { # {{{
+ baz: 'qux'
+ } # }}}
+ endclass
+ assert_equal({baz: 'qux'}, Foo.bar)
+ END
+ v9.CheckSourceSuccess(lines)
+
# Try to define a class with the same name as an existing variable
lines =<< trim END
vim9script
@@ -533,6 +545,28 @@ def Test_using_null_class()
@_ = null_class.member
END
v9.CheckDefExecAndScriptFailure(lines, ['E715: Dictionary required', 'E1363: Incomplete type'])
+
+ # Test for using a null class as a value
+ lines =<< trim END
+ vim9script
+ echo empty(null_class)
+ END
+ v9.CheckSourceFailure(lines, 'E1405: Class "" cannot be used as a value', 2)
+
+ # Test for using a null class with string()
+ lines =<< trim END
+ vim9script
+ assert_equal('class [unknown]', string(null_class))
+ END
+ v9.CheckSourceSuccess(lines)
+
+ # Test for using a null class with type() and typename()
+ lines =<< trim END
+ vim9script
+ assert_equal(12, type(null_class))
+ assert_equal('class<Unknown>', typename(null_class))
+ END
+ v9.CheckSourceSuccess(lines)
enddef
def Test_class_interface_wrong_end()
@@ -10690,4 +10724,24 @@ def Test_class_definition_in_a_function()
v9.CheckScriptFailure(lines, 'E1429: Class can only be used in a script', 1)
enddef
+" Test for using [] with a class and an object
+def Test_class_object_index()
+ var lines =<< trim END
+ vim9script
+ class A
+ endclass
+ A[10] = 1
+ END
+ v9.CheckScriptFailure(lines, 'E689: Index not allowed after a class: A[10] = 1', 4)
+
+ lines =<< trim END
+ vim9script
+ class A
+ endclass
+ var a = A.new()
+ a[10] = 1
+ END
+ v9.CheckScriptFailure(lines, 'E689: Index not allowed after a object: a[10] = 1', 5)
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim
index 7764b37..5734731 100644
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -309,6 +309,9 @@ def Test_expr2()
g:vals = [1]
endif
assert_equal([1], g:vals)
+
+ # string interpolation with ||
+ assert_equal('true', $"{0 || 1}")
END
v9.CheckDefAndScriptSuccess(lines)
@@ -415,6 +418,7 @@ def Test_expr2_fails()
v9.CheckDefExecAndScriptFailure(['var x = 3', 'if x', 'endif'], 'E1023:', 2)
v9.CheckDefAndScriptFailure(["var x = [] || false"], ['E1012: Type mismatch; expected bool but got list<any>', 'E745:'], 1)
+ v9.CheckDefAndScriptFailure(["var x = $'{false || []}'"], ['E1012: Type mismatch; expected bool but got list<any>', 'E745:'], 1)
var lines =<< trim END
vim9script
@@ -475,6 +479,9 @@ def Test_expr3()
failed = true
endif
assert_false(failed)
+
+ # string interpolation with &&
+ assert_equal('false', $"{1 && 0}")
END
v9.CheckDefAndScriptSuccess(lines)
enddef
@@ -574,6 +581,8 @@ def Test_expr3_fails()
echo true && s
END
v9.CheckDefAndScriptFailure(lines, ['E1012: Type mismatch; expected bool but got string', 'E1135: Using a String as a Bool: "asdf"'])
+
+ v9.CheckDefAndScriptFailure(["var x = $'{true && []}'"], ['E1012: Type mismatch; expected bool but got list<any>', 'E745:'], 1)
enddef
" global variables to use for tests with the "any" type
@@ -2109,6 +2118,12 @@ def Test_expr9_number()
Test()
END
v9.CheckDefAndScriptSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+ eval("10\n")
+ END
+ v9.CheckSourceScriptFailure(lines, "E488: Trailing characters: \n")
enddef
def Test_expr9_float()
@@ -2152,6 +2167,7 @@ def Test_expr9_blob()
v9.CheckDefAndScriptSuccess(lines)
v9.CheckDefAndScriptFailure(["var x = 0z123"], 'E973:', 1)
+ v9.CheckDefAndScriptFailure(["var x = null_blox"], ['E1001:', 'E121:'], 1)
enddef
def Test_expr9_string()
diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim
index b008929..d07bbfb 100644
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -4633,6 +4633,19 @@ def Run_Test_keytyped_in_nested_function()
g:StopVimInTerminal(buf)
enddef
+" Test for test_override('defcompile')
+def Test_test_override_defcompile()
+ var lines =<< trim END
+ vim9script
+ def Foo()
+ xxx
+ enddef
+ END
+ test_override('defcompile', 1)
+ v9.CheckScriptFailure(lines, 'E476: Invalid command: xxx')
+ test_override('defcompile', 0)
+enddef
+
" The following messes up syntax highlight, keep near the end.
if has('python3')
def Test_python3_command()
diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim
index 581925d..fb309cb 100644
--- a/src/testdir/test_vim9_import.vim
+++ b/src/testdir/test_vim9_import.vim
@@ -3068,7 +3068,10 @@ def Test_vim9_import_symlink()
var lines =<< trim END
vim9script
import autoload 'bar.vim'
- g:resultFunc = bar.Func()
+ def FooFunc(): string
+ return bar.Func()
+ enddef
+ g:resultFunc = FooFunc()
g:resultValue = bar.value
END
writefile(lines, 'Xto/plugin/foo.vim')
@@ -3222,4 +3225,109 @@ def Test_autoload_import_dict_func()
&rtp = save_rtp
enddef
+" Test for changing the value of an imported Dict item
+def Test_set_imported_dict_item()
+ var lines =<< trim END
+ vim9script
+ export var dict1: dict<bool> = {bflag: false}
+ export var dict2: dict<dict<bool>> = {x: {bflag: false}}
+ END
+ writefile(lines, 'XimportedDict.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+ import './XimportedDict.vim'
+ assert_equal(XimportedDict.dict1.bflag, false)
+ XimportedDict.dict1.bflag = true
+ assert_equal(XimportedDict.dict1.bflag, true)
+ XimportedDict.dict2.x.bflag = true
+ assert_equal(XimportedDict.dict2.x.bflag, true)
+ assert_equal('bool', typename(XimportedDict.dict1.bflag))
+ assert_equal('bool', typename(XimportedDict.dict2.x.bflag))
+ assert_equal('bool', typename(XimportedDict.dict2['x'].bflag))
+ assert_equal('bool', typename(XimportedDict.dict2.x['bflag']))
+
+ assert_equal(XimportedDict.dict1['bflag'], true)
+ XimportedDict.dict1['bflag'] = false
+ assert_equal(XimportedDict.dict1.bflag, false)
+ XimportedDict.dict2['x']['bflag'] = false
+ assert_equal(XimportedDict.dict2['x'].bflag, false)
+ END
+ v9.CheckScriptSuccess(lines)
+
+ lines =<< trim END
+ vim9script
+ import './XimportedDict.vim'
+ XimportedDict.dict2.x.bflag = []
+ END
+ v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected bool but got list<any>', 3)
+enddef
+
+" Test for changing the value of an imported class member
+def Test_set_imported_class_member()
+ var lines =<< trim END
+ vim9script
+ export class Config
+ public static var option = false
+ endclass
+ END
+ writefile(lines, 'XimportedClass.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+ import './XimportedClass.vim' as foo
+ type FooConfig = foo.Config
+ assert_equal(false, FooConfig.option)
+ assert_equal(false, foo.Config.option)
+ foo.Config.option = true
+ assert_equal(true, foo.Config.option)
+ assert_equal(true, FooConfig.option)
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for using an imported function from the vimrc file.  The function is
+" defined in the 'start' directory of a package.
+def Test_import_from_vimrc()
+ mkdir('Ximport/pack/foobar/start/foo/autoload', 'pR')
+ var lines =<< trim END
+ vim9script
+ export def Foo()
+ writefile(['Foo called'], 'Xoutput.log')
+ enddef
+ END
+ writefile(lines, 'Ximport/pack/foobar/start/foo/autoload/foo.vim')
+ lines =<< trim END
+ vim9script
+ set packpath+=./Ximport
+ try
+ import autoload 'foo.vim'
+ foo.Foo()
+ catch
+ writefile(['Failed to import foo.vim'], 'Xoutput.log')
+ endtry
+ qall!
+ END
+ writefile(lines, 'Xvimrc', 'D')
+ g:RunVim([], [], '-u Xvimrc')
+ assert_equal(['Foo called'], readfile('Xoutput.log'))
+ delete('Xoutput.log')
+enddef
+
+" Test for changing a locked imported variable
+def Test_import_locked_var()
+ var lines =<< trim END
+ vim9script
+ export var Foo: number = 10
+ lockvar Foo
+ END
+ writefile(lines, 'Ximportlockedvar.vim', 'D')
+ lines =<< trim END
+ vim9script
+ import './Ximportlockedvar.vim' as Bar
+ Bar.Foo = 20
+ END
+ v9.CheckScriptFailure(lines, 'E741: Value is locked: Foo', 3)
+enddef
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index a3f21be..0b17150 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -2159,6 +2159,18 @@ def Test_echo_cmd()
assert_match('^two$', g:Screenline(&lines))
v9.CheckDefFailure(['echo "xxx"# comment'], 'E488:')
+
+ # Test for echoing a script local function name
+ var lines =<< trim END
+ vim9script
+ def ScriptLocalEcho()
+ enddef
+ echo ScriptLocalEcho
+ END
+ new
+ setline(1, lines)
+ assert_match('<SNR>\d\+_ScriptLocalEcho', execute('source')->split("\n")[0])
+ bw!
enddef
def Test_echomsg_cmd()
@@ -2510,8 +2522,20 @@ def Test_for_loop()
reslist->add('x')
endfor
assert_equal(['x', 'x', 'x'], reslist)
+
+ # Test for trying to use the loop variable "_" inside the loop
+ for _ in "a"
+ assert_fails('echo _', 'E1181: Cannot use an underscore here')
+ endfor
END
v9.CheckDefAndScriptSuccess(lines)
+
+ lines =<< trim END
+ for i : number : [1, 2]
+ echo i
+ endfor
+ END
+ v9.CheckSourceDefAndScriptFailure(lines, 'E1059: No white space allowed before colon: : [1, 2]', 1)
enddef
def Test_for_loop_list_of_lists()
@@ -4551,11 +4575,11 @@ def Run_Test_debug_with_lambda()
Func()
END
writefile(lines, 'XdebugFunc', 'D')
- var buf = g:RunVimInTerminal('-S XdebugFunc', {rows: 6, wait_for_ruler: 0})
- g:WaitForAssert(() => assert_match('^>', term_getline(buf, 6)))
+ var buf = g:RunVimInTerminal('-S XdebugFunc', {rows: 10, wait_for_ruler: 0})
+ g:WaitForAssert(() => assert_match('^>', term_getline(buf, 10)))
term_sendkeys(buf, "cont\<CR>")
- g:WaitForAssert(() => assert_match('\[0\]', term_getline(buf, 5)))
+ g:WaitForAssert(() => assert_match('\[0\]', term_getline(buf, 9)))
g:StopVimInTerminal(buf)
enddef
@@ -4586,12 +4610,12 @@ def Run_Test_debug_running_out_of_lines()
Crash()
END
writefile(lines, 'XdebugFunc', 'D')
- var buf = g:RunVimInTerminal('-S XdebugFunc', {rows: 6, wait_for_ruler: 0})
- g:WaitForAssert(() => assert_match('^>', term_getline(buf, 6)))
+ var buf = g:RunVimInTerminal('-S XdebugFunc', {rows: 10, wait_for_ruler: 0})
+ g:WaitForAssert(() => assert_match('^>', term_getline(buf, 10)))
term_sendkeys(buf, "next\<CR>")
g:TermWait(buf)
- g:WaitForAssert(() => assert_match('^>', term_getline(buf, 6)))
+ g:WaitForAssert(() => assert_match('^>', term_getline(buf, 10)))
term_sendkeys(buf, "cont\<CR>")
g:TermWait(buf)
@@ -5002,7 +5026,7 @@ def Test_invalid_type_in_for()
enddef
defcompile
END
- v9.CheckSourceFailure(lines, 'E1010: Type not recognized: x in range(10)', 1)
+ v9.CheckSourceFailure(lines, 'E1010: Type not recognized: x', 1)
enddef
" Test for using a line break between the variable name and the type in a for
@@ -5055,6 +5079,56 @@ def Test_eval_lambda_block()
v9.CheckSourceSuccess(lines)
enddef
+" Test for using various null values
+def Test_null_values()
+ var lines =<< trim END
+ var nullValues = [
+ [null, 1, 'null', 7, 'special'],
+ [null_blob, 1, '0z', 10, 'blob'],
+ [null_channel, 1, 'channel fail', 9, 'channel'],
+ [null_dict, 1, '{}', 4, 'dict<any>'],
+ [null_function, 1, "function('')", 2, 'func(...): unknown'],
+ [null_job, 1, 'no process', 8, 'job'],
+ [null_list, 1, '[]', 3, 'list<any>'],
+ [null_object, 1, 'object of [unknown]', 13, 'object<Unknown>'],
+ [null_partial, 1, "function('')", 2, 'func(...): unknown'],
+ [null_string, 1, "''", 1, 'string']
+ ]
+
+ for [Val, emptyExp, stringExp, typeExp, typenameExp] in nullValues
+ assert_equal(emptyExp, empty(Val))
+ assert_equal(stringExp, string(Val))
+ assert_equal(typeExp, type(Val))
+ assert_equal(typenameExp, typename(Val))
+ assert_equal(Val, copy(Val))
+ assert_equal(-1, test_refcount(Val))
+ endfor
+ END
+ v9.CheckSourceDefAndScriptSuccess(lines)
+enddef
+
+" Test for using an unknown type in a typecast
+def Test_unknown_type_in_typecast()
+ var lines =<< trim END
+ vim9script
+ var a = <MyType>b
+ END
+ v9.CheckSourceFailure(lines, 'E1010: Type not recognized: MyType', 2)
+
+ lines =<< trim END
+ vim9script
+ var Fn = <funcx(number, number): number>b
+ END
+ v9.CheckSourceFailure(lines, 'E1010: Type not recognized: funcx(number, number): number', 2)
+
+ # Wrong type in a type cast
+ lines =<< trim END
+ vim9script
+ var i: number = <number>true
+ END
+ v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got bool', 2)
+enddef
+
" Keep this last, it messes up highlighting.
def Test_substitute_cmd()
new
diff --git a/src/testdir/test_vim9_typealias.vim b/src/testdir/test_vim9_typealias.vim
index cf540c2..2792d45 100644
--- a/src/testdir/test_vim9_typealias.vim
+++ b/src/testdir/test_vim9_typealias.vim
@@ -363,7 +363,7 @@ def Test_typealias_import()
var myNum: A.SomeType = 10
END
- v9.CheckScriptFailure(lines, 'E1010: Type not recognized: A.SomeType = 10', 4)
+ v9.CheckScriptFailure(lines, 'E1010: Type not recognized: A.SomeType', 4)
# Use a type alias that is not exported
lines =<< trim END
diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim
index 1f4a72d..7aab271 100644
--- a/src/testdir/test_viminfo.vim
+++ b/src/testdir/test_viminfo.vim
@@ -1299,4 +1299,34 @@ func Test_viminfo_merge_old_jumplist()
bw!
endfunc
+func Test_viminfo_oldfiles_filter()
+ let v:oldfiles = []
+ let _viminfofile = &viminfofile
+ let &viminfofile=''
+ let lines = [
+ \ '# comment line',
+ \ '*encoding=utf-8',
+ \ "> /tmp/vimrc_one.vim",
+ \ "\t\"\t11\t0",
+ \ "",
+ \ "> /tmp/foobar.txt",
+ \ "\t\"\t11\t0",
+ \ "",
+ \ ]
+ call writefile(lines, 'Xviminfo1', 'D')
+ rviminfo! Xviminfo1
+ new
+ " filter returns a single item
+ let a = execute('filter /vim/ oldfiles')->split('\n')
+ call assert_equal(1, len(a))
+ " filter returns more than a single match
+ let a = execute('filter #tmp# oldfiles')->split('\n')
+ call assert_equal(2, len(a))
+ " don't get prompted for the file, but directly open it
+ filter /vim/ browse oldfiles
+ call assert_equal("/tmp/vimrc_one.vim", expand("%"))
+ bw
+ let &viminfofile = _viminfofile
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim
index 0c4aedb..8976779 100644
--- a/src/testdir/test_vimscript.vim
+++ b/src/testdir/test_vimscript.vim
@@ -7509,6 +7509,14 @@ func Test_for_over_string()
let res ..= c .. '-'
endfor
call assert_equal('', res)
+
+ " Test for using "_" as the loop variable
+ let i = 0
+ let s = 'abc'
+ for _ in s
+ call assert_equal(s[i], _)
+ let i += 1
+ endfor
endfunc
" Test for deeply nested :source command {{{1
diff --git a/src/testdir/test_virtualedit.vim b/src/testdir/test_virtualedit.vim
index 48d4aa0..3d3fdd0 100644
--- a/src/testdir/test_virtualedit.vim
+++ b/src/testdir/test_virtualedit.vim
@@ -709,5 +709,27 @@ func Test_virtualedit_replace_after_tab()
bwipe!
endfunc
+" Test that setpos('.') and cursor() behave the same for v:maxcol
+func Test_virtualedit_set_cursor_pos_maxcol()
+ new
+ set virtualedit=all
+
+ call setline(1, 'foobar')
+ exe "normal! V\<Esc>"
+ call assert_equal([0, 1, 1, 0], getpos("'<"))
+ call assert_equal([0, 1, v:maxcol, 0], getpos("'>"))
+ let pos = getpos("'>")
+
+ call cursor(1, 1)
+ call setpos('.', pos)
+ call assert_equal([0, 1, 7, 0], getpos('.'))
+
+ call cursor(1, 1)
+ call cursor(pos[1:])
+ call assert_equal([0, 1, 7, 0], getpos('.'))
+
+ set virtualedit&
+ bwipe!
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim
index adcc3e9..3750ebf 100644
--- a/src/testdir/test_visual.vim
+++ b/src/testdir/test_visual.vim
@@ -1631,6 +1631,22 @@ func Test_visual_substitute_visual()
bwipe!
endfunc
+func Test_virtualedit_exclusive_selection()
+ new
+ set virtualedit=all selection=exclusive
+
+ call setline(1, "a\tb")
+ normal! 0v8ly
+ call assert_equal("a\t", getreg('"'))
+ normal! 0v6ly
+ call assert_equal('a ', getreg('"'))
+ normal! 06lv2ly
+ call assert_equal(' ', getreg('"'))
+
+ set virtualedit& selection&
+ bwipe!
+endfunc
+
func Test_visual_getregion()
let lines =<< trim END
new
@@ -1640,18 +1656,52 @@ func Test_visual_getregion()
#" Visual mode
call cursor(1, 1)
call feedkeys("\<ESC>vjl", 'tx')
+
call assert_equal(['one', 'tw'],
\ 'v'->getpos()->getregion(getpos('.')))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 0]]
+ \ ],
+ \ 'v'->getpos()->getregionpos(getpos('.')))
+
call assert_equal(['one', 'tw'],
\ '.'->getpos()->getregion(getpos('v')))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 0]]
+ \ ],
+ \ '.'->getpos()->getregionpos(getpos('v')))
+
call assert_equal(['o'],
\ 'v'->getpos()->getregion(getpos('v')))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 1, 0]],
+ \ ],
+ \ 'v'->getpos()->getregionpos(getpos('v')))
+
call assert_equal(['w'],
\ '.'->getpos()->getregion(getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 0]],
+ \ ],
+ \ '.'->getpos()->getregionpos(getpos('.'), {'type': 'v' }))
+
call assert_equal(['one', 'two'],
\ getpos('.')->getregion(getpos('v'), {'type': 'V' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ ],
+ \ getpos('.')->getregionpos(getpos('v'), {'type': 'V' }))
+
call assert_equal(['on', 'tw'],
\ getpos('.')->getregion(getpos('v'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 0]],
+ \ ],
+ \ getpos('.')->getregionpos(getpos('v'), {'type': "\<C-v>" }))
#" Line visual mode
call cursor(1, 1)
@@ -1698,6 +1748,9 @@ func Test_visual_getregion()
\ "'a"->getpos()->getregion(getpos("'a"), {'type': 'V' }))
call assert_equal(['one', 'two'],
\ "."->getpos()->getregion(getpos("'a"), {'type': "\<c-v>" }))
+ call feedkeys("\<ESC>jVj\<ESC>", 'tx')
+ call assert_equal(['two', 'three'], getregion(getpos("'<"), getpos("'>")))
+ call assert_equal(['two', 'three'], getregion(getpos("'>"), getpos("'<")))
#" Using List
call cursor(1, 1)
@@ -1717,24 +1770,185 @@ func Test_visual_getregion()
call feedkeys("\<ESC>Vjj", 'tx')
call assert_equal(['one', 'two', 'three'],
\ getregion(getpos('v'), getpos('.'), {'type': 'V' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'V' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 6, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': 'V', 'eol': v:true }))
#" Multiline with block visual mode
call cursor(1, 1)
call feedkeys("\<ESC>\<C-v>jj", 'tx')
call assert_equal(['o', 't', 't'],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 1, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 1, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 1, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
call cursor(1, 1)
call feedkeys("\<ESC>\<C-v>jj$", 'tx')
call assert_equal(['one', 'two', 'three'],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': "\<C-v>", 'eol': v:true }))
#" 'virtualedit'
set virtualedit=all
+
call cursor(1, 1)
call feedkeys("\<ESC>\<C-v>10ljj$", 'tx')
call assert_equal(['one ', 'two ', 'three '],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 3]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 3]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 6, 1]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': "\<C-v>", 'eol': v:true }))
+
+ call cursor(3, 5)
+ call feedkeys("\<ESC>\<C-v>hkk", 'tx')
+ call assert_equal([' ', ' ', 'ee'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 0, 0], [bufnr('%'), 1, 0, 0]],
+ \ [[bufnr('%'), 2, 0, 0], [bufnr('%'), 2, 0, 0]],
+ \ [[bufnr('%'), 3, 4, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 4, 0], [bufnr('%'), 1, 4, 2]],
+ \ [[bufnr('%'), 2, 4, 0], [bufnr('%'), 2, 4, 2]],
+ \ [[bufnr('%'), 3, 4, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': "\<C-v>", 'eol': v:true }))
+
+ call cursor(3, 5)
+ call feedkeys("\<ESC>\<C-v>kk", 'tx')
+ call assert_equal([' ', ' ', 'e'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 0, 0], [bufnr('%'), 1, 0, 0]],
+ \ [[bufnr('%'), 2, 0, 0], [bufnr('%'), 2, 0, 0]],
+ \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 4, 1], [bufnr('%'), 1, 4, 2]],
+ \ [[bufnr('%'), 2, 4, 1], [bufnr('%'), 2, 4, 2]],
+ \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': "\<C-v>", 'eol': v:true }))
+
+ call cursor(1, 3)
+ call feedkeys("\<ESC>vjj4l", 'tx')
+ call assert_equal(['e', 'two', 'three '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 4, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 6, 2]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': 'v', 'eol': v:true }))
+
+ call cursor(1, 3)
+ call feedkeys("\<ESC>lvjj3l", 'tx')
+ call assert_equal(['', 'two', 'three '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 0, 0], [bufnr('%'), 1, 0, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 4, 0], [bufnr('%'), 1, 4, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 6, 2]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': 'v', 'eol': v:true }))
+
+ call cursor(3, 5)
+ call feedkeys("\<ESC>v3l", 'tx')
+ call assert_equal(['e '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 6, 3]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': 'v', 'eol': v:true }))
+
+ call cursor(3, 5)
+ call feedkeys("\<ESC>lv3l", 'tx')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 3, 6, 0], [bufnr('%'), 3, 6, 4]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': 'v', 'eol': v:true }))
+
+ call cursor(3, 5)
+ call feedkeys("\<ESC>3lv3l", 'tx')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 3, 6, 2], [bufnr('%'), 3, 6, 6]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': 'v', 'eol': v:true }))
+
set virtualedit&
#" using wrong types for positions
@@ -1743,9 +1957,21 @@ func Test_visual_getregion()
call assert_fails("call getregion(1, 2)", 'E1211:')
call assert_fails("call getregion(getpos('.'), {})", 'E1211:')
call assert_fails(':echo "."->getpos()->getregion("$", [])', 'E1211:')
+ call assert_fails("call getregionpos(1, 2)", 'E1211:')
+ call assert_fails("call getregionpos(getpos('.'), {})", 'E1211:')
+ call assert_fails(':echo "."->getpos()->getregionpos("$", [])', 'E1211:')
#" using invalid value for "type"
call assert_fails("call getregion(getpos('.'), getpos('.'), {'type': '' })", 'E475:')
+ call assert_fails("call getregionpos(getpos('.'), getpos('.'), {'type': '' })", 'E475:')
+ call assert_fails("call getregion(getpos('.'), getpos('.'), {'type': 'v0' })", 'E475:')
+ call assert_fails("call getregionpos(getpos('.'), getpos('.'), {'type': 'v0' })", 'E475:')
+ call assert_fails("call getregion(getpos('.'), getpos('.'), {'type': 'V0' })", 'E475:')
+ call assert_fails("call getregionpos(getpos('.'), getpos('.'), {'type': 'V0' })", 'E475:')
+ call assert_fails("call getregion(getpos('.'), getpos('.'), {'type': '\<C-v>0' })", 'E475:')
+ call assert_fails("call getregionpos(getpos('.'), getpos('.'), {'type': '\<C-v>0' })", 'E475:')
+ call assert_fails("call getregion(getpos('.'), getpos('.'), {'type': '\<C-v>1:' })", 'E475:')
+ call assert_fails("call getregionpos(getpos('.'), getpos('.'), {'type': '\<C-v>1:' })", 'E475:')
#" using a mark from another buffer to current buffer
new
@@ -1756,13 +1982,20 @@ func Test_visual_getregion()
call assert_equal([g:buf, 10, 1, 0], getpos("'A"))
call assert_equal([], getregion(getpos('.'), getpos("'A"), {'type': 'v' }))
call assert_equal([], getregion(getpos("'A"), getpos('.'), {'type': 'v' }))
+ call assert_equal([], getregionpos(getpos('.'), getpos("'A"), {'type': 'v' }))
+ call assert_equal([], getregionpos(getpos("'A"), getpos('.'), {'type': 'v' }))
#" using two marks from another buffer
wincmd p
normal! GmB
wincmd p
call assert_equal([g:buf, 10, 1, 0], getpos("'B"))
- call assert_equal(['9'], getregion(getpos("'B"), getpos("'A"), {'type': 'v' }))
+ call assert_equal(['9'],
+ \ getregion(getpos("'B"), getpos("'A"), {'type': 'v' }))
+ call assert_equal([
+ \ [[g:buf, 10, 1, 0], [g:buf, 10, 1, 0]],
+ \ ],
+ \ getregionpos(getpos("'B"), getpos("'A"), {'type': 'v' }))
#" using two positions from another buffer
for type in ['v', 'V', "\<C-V>"]
@@ -1773,6 +2006,12 @@ func Test_visual_getregion()
call assert_equal(range(10)->mapnew('string(v:val)'),
\ getregion([g:buf, 10, 2, 0], [g:buf, 1, 1, 0],
\ {'type': type, 'exclusive': exclusive }))
+ call assert_equal(range(1, 10)->mapnew('repeat([[g:buf, v:val, 1, 0]], 2)'),
+ \ getregionpos([g:buf, 1, 1, 0], [g:buf, 10, 2, 0],
+ \ {'type': type, 'exclusive': exclusive }))
+ call assert_equal(range(1, 10)->mapnew('repeat([[g:buf, v:val, 1, 0]], 2)'),
+ \ getregionpos([g:buf, 10, 2, 0], [g:buf, 1, 1, 0],
+ \ {'type': type, 'exclusive': exclusive }))
endfor
endfor
@@ -1785,17 +2024,18 @@ func Test_visual_getregion()
call assert_fails('call getregion([g:buf, 10, 0, 0], [g:buf, 1, 1, 0])', 'E964:')
call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 10, 3, 0])', 'E964:')
call assert_fails('call getregion([g:buf, 10, 3, 0], [g:buf, 1, 1, 0])', 'E964:')
+ call assert_fails('call getregion([g:buf, 1, 0, 0], [g:buf, 1, 1, 0])', 'E964:')
+ call assert_fails('call getregion([g:buf, 1, 1, 0], [g:buf, 1, 0, 0])', 'E964:')
#" using invalid buffer
call assert_fails('call getregion([10000, 10, 1, 0], [10000, 10, 1, 0])', 'E681:')
exe $':{g:buf}bwipe!'
unlet g:buf
+ bwipe!
END
call v9.CheckLegacyAndVim9Success(lines)
- bwipe!
-
let lines =<< trim END
#" Selection in starts or ends in the middle of a multibyte character
new
@@ -1804,35 +2044,119 @@ func Test_visual_getregion()
\ "\U0001f1e6\u00ab\U0001f1e7\u00ab\U0001f1e8\u00ab\U0001f1e9",
\ "1234567890"
\ ])
+
call cursor(1, 3)
call feedkeys("\<Esc>\<C-v>ljj", 'xt')
call assert_equal(['cd', "\u00ab ", '34'],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 4, 0]],
+ \ [[bufnr('%'), 2, 5, 0], [bufnr('%'), 2, 7, 1]],
+ \ [[bufnr('%'), 3, 3, 0], [bufnr('%'), 3, 4, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
call cursor(1, 4)
call feedkeys("\<Esc>\<C-v>ljj", 'xt')
call assert_equal(['de', "\U0001f1e7", '45'],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 4, 0], [bufnr('%'), 1, 5, 0]],
+ \ [[bufnr('%'), 2, 7, 0], [bufnr('%'), 2, 10, 0]],
+ \ [[bufnr('%'), 3, 4, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
call cursor(1, 5)
call feedkeys("\<Esc>\<C-v>jj", 'xt')
call assert_equal(['e', ' ', '5'],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 5, 0], [bufnr('%'), 1, 5, 0]],
+ \ [[bufnr('%'), 2, 7, 1], [bufnr('%'), 2, 7, 2]],
+ \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal(['efghijk«', '🇦«🇧«🇨«🇩', '12345'],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 5, 0], [bufnr('%'), 1, 13, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 22, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 5, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 5)
+ call feedkeys("\<Esc>\<C-v>5l2j", 'xt')
+ call assert_equal(['efghij', ' «🇨« ', '567890'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 5, 0], [bufnr('%'), 1, 10, 0]],
+ \ [[bufnr('%'), 2, 7, 1], [bufnr('%'), 2, 19, 1]],
+ \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 10, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+ call cursor(1, 4)
+ call feedkeys("\<Esc>\<C-v>02j", 'xt')
+ call assert_equal(['abcd', '🇦« ', '1234'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 7, 1]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 4, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+ #" characterwise selection with multibyte chars
call cursor(1, 1)
call feedkeys("\<Esc>vj", 'xt')
call assert_equal(['abcdefghijk«', "\U0001f1e6"],
\ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 13, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ set selection=exclusive
+ call feedkeys('l', 'xt')
+ call assert_equal(['abcdefghijk«', "\U0001f1e6"],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 13, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 4, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
#" marks on multibyte chars
- :set selection=exclusive
call setpos("'a", [0, 1, 11, 0])
call setpos("'b", [0, 2, 16, 0])
call setpos("'c", [0, 2, 0, 0])
call cursor(1, 1)
+
call assert_equal(['ghijk', '🇨«🇩'],
- \ getregion(getpos("'a"), getpos("'b"), {'type': "\<c-v>" }))
+ \ getregion(getpos("'a"), getpos("'b"), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 7, 0], [bufnr('%'), 1, 11, 0]],
+ \ [[bufnr('%'), 2, 13, 0], [bufnr('%'), 2, 22, 0]],
+ \ ],
+ \ getregionpos(getpos("'a"), getpos("'b"), {'type': "\<C-v>" }))
+
call assert_equal(['k«', '🇦«🇧«🇨'],
\ getregion(getpos("'a"), getpos("'b"), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 11, 0], [bufnr('%'), 1, 13, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 16, 0]],
+ \ ],
+ \ getregionpos(getpos("'a"), getpos("'b"), {'type': 'v' }))
+
call assert_equal(['k«'],
\ getregion(getpos("'a"), getpos("'c"), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 11, 0], [bufnr('%'), 1, 13, 0]],
+ \ ],
+ \ getregionpos(getpos("'a"), getpos("'c"), {'type': 'v' }))
#" use inclusive selection, although 'selection' is exclusive
call setpos("'a", [0, 1, 11, 0])
@@ -1855,12 +2179,12 @@ func Test_visual_getregion()
call assert_equal(['abcdefghijk«'],
\ getregion(getpos("'a"), getpos("'b"),
\ {'type': 'V', 'exclusive': 1 }))
- :set selection&
+
+ set selection&
+ bwipe!
END
call v9.CheckLegacyAndVim9Success(lines)
- bwipe!
-
let lines =<< trim END
#" Exclusive selection
new
@@ -1891,51 +2215,346 @@ func Test_visual_getregion()
call assert_equal(["c", "x\tz"],
\ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
set selection&
+ bwipe!
#" Exclusive selection 2
new
call setline(1, ["a\tc", "x\tz", '', ''])
+
call cursor(1, 1)
call feedkeys("\<Esc>v2l", 'xt')
call assert_equal(["a\t"],
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
+
call cursor(1, 1)
call feedkeys("\<Esc>v$G", 'xt')
call assert_equal(["a\tc", "x\tz", ''],
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
+
call cursor(1, 1)
call feedkeys("\<Esc>v$j", 'xt')
call assert_equal(["a\tc", "x\tz"],
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
+
call cursor(1, 1)
call feedkeys("\<Esc>\<C-v>$j", 'xt')
call assert_equal(["a\tc", "x\tz"],
\ getregion(getpos('v'), getpos('.'),
\ {'exclusive': v:true, 'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'exclusive': v:true, 'type': "\<C-v>" }))
+
call cursor(1, 1)
call feedkeys("\<Esc>\<C-v>$G", 'xt')
call assert_equal(["a", "x", '', ''],
\ getregion(getpos('v'), getpos('.'),
\ {'exclusive': v:true, 'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 1, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 1, 0]],
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+ \ [[bufnr('%'), 4, 0, 0], [bufnr('%'), 4, 0, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'exclusive': v:true, 'type': "\<C-v>" }))
+
call cursor(1, 1)
call feedkeys("\<Esc>wv2j", 'xt')
call assert_equal(["c", "x\tz"],
\ getregion(getpos('v'), getpos('.'), {'exclusive': v:true }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'exclusive': v:true }))
- #" virtualedit
+ #" 'virtualedit' with exclusive selection
set selection=exclusive
set virtualedit=all
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>vj", 'xt')
+ call assert_equal(["a\tc"],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
call cursor(1, 1)
- call feedkeys("\<Esc>2lv2lj", 'xt')
+ call feedkeys("\<Esc>v8l", 'xt')
+ call assert_equal(["a\t"],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v6l", 'xt')
+ call assert_equal(['a '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 5]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>6lv2l", 'xt')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 2, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>lv2l", 'xt')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 2]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>2lv2l", 'xt')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 3]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call feedkeys('j', 'xt')
call assert_equal([' c', 'x '],
\ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 3]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>6l\<C-v>2lj", 'xt')
+ call assert_equal([' ', ' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 2, 7]],
+ \ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 2, 7]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>l\<C-v>2l2j", 'xt')
+ call assert_equal([' ', ' ', ' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 2]],
+ \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 2]],
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 2]],
+ \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 2]],
+ \ [[bufnr('%'), 3, 1, 1], [bufnr('%'), 3, 1, 3]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': "\<C-v>", "eol": v:true }))
+
call cursor(1, 1)
call feedkeys("\<Esc>2l\<C-v>2l2j", 'xt')
call assert_equal([' ', ' ', ' '],
\ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
- set virtualedit&
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 3]],
+ \ [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 3]],
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 3]],
+ \ [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 3]],
+ \ [[bufnr('%'), 3, 1, 2], [bufnr('%'), 3, 1, 4]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': "\<C-v>", "eol": v:true }))
+
+ #" 'virtualedit' with inclusive selection
set selection&
+ call cursor(1, 1)
+ call feedkeys("\<Esc>vj", 'xt')
+ call assert_equal(["a\tc", 'x'],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 1, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v8l", 'xt')
+ call assert_equal(["a\tc"],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>v6l", 'xt')
+ call assert_equal(['a '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 2, 6]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>6lv2l", 'xt')
+ call assert_equal([' c'],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>lv2l", 'xt')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 3]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>2lv2l", 'xt')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 4]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call feedkeys('j', 'xt')
+ call assert_equal([' c', 'x '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 2, 4]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>6l\<C-v>2lj", 'xt')
+ call assert_equal([' c', ' z'],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 5], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>l\<C-v>2l2j", 'xt')
+ call assert_equal([' ', ' ', ' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 3]],
+ \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 3]],
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 2, 3]],
+ \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 2, 3]],
+ \ [[bufnr('%'), 3, 1, 1], [bufnr('%'), 3, 1, 4]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': "\<C-v>", "eol": v:true }))
+
+ call cursor(1, 1)
+ call feedkeys("\<Esc>2l\<C-v>2l2j", 'xt')
+ call assert_equal([' ', ' ', ' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 4]],
+ \ [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 4]],
+ \ [[bufnr('%'), 3, 0, 0], [bufnr('%'), 3, 0, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 2, 1], [bufnr('%'), 1, 2, 4]],
+ \ [[bufnr('%'), 2, 2, 1], [bufnr('%'), 2, 2, 4]],
+ \ [[bufnr('%'), 3, 1, 2], [bufnr('%'), 3, 1, 5]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': "\<C-v>", "eol": v:true }))
+
+ set virtualedit&
+ bwipe!
+ END
+ call v9.CheckLegacyAndVim9Success(lines)
+
+ let lines =<< trim END
+ #" 'virtualedit' with TABs at end of line
+ new
+ set virtualedit=all
+ call setline(1, ["\t", "a\t", "aa\t"])
+
+ call feedkeys("gg06l\<C-v>3l2j", 'xt')
+ call assert_equal([' ', ' ', ' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 6], [bufnr('%'), 1, 1, 0]],
+ \ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 2, 0]],
+ \ [[bufnr('%'), 3, 3, 4], [bufnr('%'), 3, 3, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': "\<C-v>" }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 6], [bufnr('%'), 1, 2, 2]],
+ \ [[bufnr('%'), 2, 2, 5], [bufnr('%'), 2, 3, 2]],
+ \ [[bufnr('%'), 3, 3, 4], [bufnr('%'), 3, 4, 2]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': "\<C-v>", "eol": v:true }))
+
+ call feedkeys("gg06lv3l", 'xt')
+ call assert_equal([' '],
+ \ getregion(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 6], [bufnr('%'), 1, 1, 0]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'), {'type': 'v' }))
+ call assert_equal([
+ \ [[bufnr('%'), 1, 1, 6], [bufnr('%'), 1, 2, 2]],
+ \ ],
+ \ getregionpos(getpos('v'), getpos('.'),
+ \ {'type': 'v', "eol": v:true }))
+
+ set virtualedit&
bwipe!
END
call v9.CheckLegacyAndVim9Success(lines)
@@ -1955,4 +2574,144 @@ func Test_getregion_invalid_buf()
bwipe!
endfunc
+func Test_getregion_after_yank()
+ func! Check_Results(type)
+ call assert_equal(g:expected_region,
+ \ getregion(getpos("'["), getpos("']"), #{ type: a:type }))
+ call assert_equal(g:expected_regionpos,
+ \ getregionpos(getpos("'["), getpos("']"), #{ type: a:type }))
+ call assert_equal(g:expected_region,
+ \ getregion(getpos("']"), getpos("'["), #{ type: a:type }))
+ call assert_equal(g:expected_regionpos,
+ \ getregionpos(getpos("']"), getpos("'["), #{ type: a:type }))
+ let g:checked = 1
+ endfunc
+
+ autocmd TextYankPost *
+ \ : if v:event.operator ==? 'y'
+ \ | call Check_Results(v:event.regtype)
+ \ | endif
+
+ new
+ call setline(1, ['abcd', 'efghijk', 'lmn'])
+
+ let g:expected_region = ['abcd']
+ let g:expected_regionpos = [
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 0]],
+ \ ]
+ let g:checked = 0
+ normal yy
+ call assert_equal(1, g:checked)
+ call Check_Results(getregtype('"'))
+
+ let g:expected_region = ['cd', 'ghijk', 'n']
+ let g:expected_regionpos = [
+ \ [[bufnr('%'), 1, 3, 0], [bufnr('%'), 1, 4, 0]],
+ \ [[bufnr('%'), 2, 3, 0], [bufnr('%'), 2, 7, 0]],
+ \ [[bufnr('%'), 3, 3, 0], [bufnr('%'), 3, 3, 0]],
+ \ ]
+ let g:checked = 0
+ call feedkeys("gg0ll\<C-V>jj$y", 'tx')
+ call assert_equal(1, g:checked)
+ call Check_Results(getregtype('"'))
+ call assert_equal(g:expected_region, getreg('"', v:true, v:true))
+
+ let g:expected_region = ['bc', 'fg', 'mn']
+ let g:expected_regionpos = [
+ \ [[bufnr('%'), 1, 2, 0], [bufnr('%'), 1, 3, 0]],
+ \ [[bufnr('%'), 2, 2, 0], [bufnr('%'), 2, 3, 0]],
+ \ [[bufnr('%'), 3, 2, 0], [bufnr('%'), 3, 3, 0]],
+ \ ]
+ let g:checked = 0
+ call feedkeys("gg0l\<C-V>jjly", 'tx')
+ call assert_equal(1, g:checked)
+ call Check_Results(getregtype('"'))
+ call assert_equal(g:expected_region, getreg('"', v:true, v:true))
+
+ bwipe!
+
+ new
+ let lines = ['asdfghjkl', '«口=口»', 'qwertyuiop', '口口=口口', 'zxcvbnm']
+ call setline(1, lines)
+
+ let g:expected_region = lines
+ let g:expected_regionpos = [
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 9, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 11, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 10, 0]],
+ \ [[bufnr('%'), 4, 1, 0], [bufnr('%'), 4, 13, 0]],
+ \ [[bufnr('%'), 5, 1, 0], [bufnr('%'), 5, 7, 0]],
+ \ ]
+ let g:checked = 0
+ call feedkeys('ggyG', 'tx')
+ call assert_equal(1, g:checked)
+ call Check_Results(getregtype('"'))
+ call assert_equal(g:expected_region, getreg('"', v:true, v:true))
+
+ let g:expected_region = ['=口»', 'qwertyuiop', '口口=口']
+ let g:expected_regionpos = [
+ \ [[bufnr('%'), 2, 6, 0], [bufnr('%'), 2, 11, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 10, 0]],
+ \ [[bufnr('%'), 4, 1, 0], [bufnr('%'), 4, 10, 0]],
+ \ ]
+ let g:checked = 0
+ call feedkeys('2gg02lv2j2ly', 'tx')
+ call assert_equal(1, g:checked)
+ call Check_Results(getregtype('"'))
+ call assert_equal(g:expected_region, getreg('"', v:true, v:true))
+
+ let g:expected_region = ['asdf', '«口=', 'qwer', '口口', 'zxcv']
+ let g:expected_regionpos = [
+ \ [[bufnr('%'), 1, 1, 0], [bufnr('%'), 1, 4, 0]],
+ \ [[bufnr('%'), 2, 1, 0], [bufnr('%'), 2, 6, 0]],
+ \ [[bufnr('%'), 3, 1, 0], [bufnr('%'), 3, 4, 0]],
+ \ [[bufnr('%'), 4, 1, 0], [bufnr('%'), 4, 6, 0]],
+ \ [[bufnr('%'), 5, 1, 0], [bufnr('%'), 5, 4, 0]],
+ \ ]
+ let g:checked = 0
+ call feedkeys("G0\<C-V>3l4ky", 'tx')
+ call assert_equal(1, g:checked)
+ call Check_Results(getregtype('"'))
+ call assert_equal(g:expected_region, getreg('"', v:true, v:true))
+
+ let g:expected_region = ['ghjkl', '口»', 'tyuiop', '=口口', 'bnm']
+ let g:expected_regionpos = [
+ \ [[bufnr('%'), 1, 5, 0], [bufnr('%'), 1, 9, 0]],
+ \ [[bufnr('%'), 2, 7, 0], [bufnr('%'), 2, 11, 0]],
+ \ [[bufnr('%'), 3, 5, 0], [bufnr('%'), 3, 10, 0]],
+ \ [[bufnr('%'), 4, 7, 0], [bufnr('%'), 4, 13, 0]],
+ \ [[bufnr('%'), 5, 5, 0], [bufnr('%'), 5, 7, 0]],
+ \ ]
+ let g:checked = 0
+ call feedkeys("G04l\<C-V>$4ky", 'tx')
+ call assert_equal(1, g:checked)
+ call Check_Results(getregtype('"'))
+ call assert_equal(g:expected_region, getreg('"', v:true, v:true))
+
+ bwipe!
+
+ unlet g:expected_region
+ unlet g:expected_regionpos
+ unlet g:checked
+ autocmd! TextYankPost
+ delfunc Check_Results
+endfunc
+
+func Test_visual_block_cursor_delete()
+ new
+ call setline(1, 'ab')
+ exe ":norm! $\<c-v>hI\<Del>\<ESC>"
+ call assert_equal(['b'], getline(1, 1))
+ bwipe!
+endfunc
+
+func Test_visual_block_cursor_insert_enter()
+ new
+ call setline(1, ['asdf asdf', 'asdf asdf', 'asdf asdf', 'asdf asdf'])
+ call cursor(1, 5)
+ exe ":norm! \<c-v>3jcw\<cr>"
+ call assert_equal(['asdfw', 'asdf', 'asdfasdf', 'asdfasdf', 'asdfasdf'], getline(1, '$'))
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/testdir/test_xdg.vim b/src/testdir/test_xdg.vim
index 6f35b72..b9ec3c7 100644
--- a/src/testdir/test_xdg.vim
+++ b/src/testdir/test_xdg.vim
@@ -80,6 +80,7 @@ func Test_xdg_runtime_files()
call assert_match('XfakeHOME/\.vimrc', $MYVIMRC)
call filter(g:, {idx, _ -> idx =~ '^rc'})
call assert_equal(#{rc_one: 'one', rc: '.vimrc'}, g:)
+ call assert_match('XfakeHOME/\.vim/view', &viewdir)
call writefile(v:errors, 'Xresult')
quit
END
@@ -94,6 +95,7 @@ func Test_xdg_runtime_files()
call assert_match('XfakeHOME/\.vim/vimrc', $MYVIMRC)
call filter(g:, {idx, _ -> idx =~ '^rc'})
call assert_equal(#{rc_two: 'two', rc: '.vim/vimrc'}, g:)
+ call assert_match('XfakeHOME/\.vim/view', &viewdir)
call writefile(v:errors, 'Xresult')
quit
END
@@ -112,6 +114,7 @@ func Test_xdg_runtime_files()
call assert_match('XfakeHOME/\.config/vim/vimrc', $MYVIMRC, msg)
call filter(g:, {idx, _ -> idx =~ '^rc'})
call assert_equal(#{rc_three: 'three', rc: '.config/vim/vimrc'}, g:)
+ call assert_match('XfakeHOME/\.config/vim/view', &viewdir)
call writefile(v:errors, 'Xresult')
quit
END
@@ -128,6 +131,7 @@ func Test_xdg_runtime_files()
call assert_match('XfakeHOME/xdg/vim/vimrc', $MYVIMRC, msg)
call filter(g:, {idx, _ -> idx =~ '^rc'})
call assert_equal(#{rc_four: 'four', rc: 'xdg/vim/vimrc'}, g:)
+ call assert_match('XfakeHOME/xdg/vim/view, &viewdir)
call writefile(v:errors, 'Xresult')
quit
END
diff --git a/src/testdir/test_xxd.vim b/src/testdir/test_xxd.vim
index 7a2771e..a91a1fc 100644
--- a/src/testdir/test_xxd.vim
+++ b/src/testdir/test_xxd.vim
@@ -411,6 +411,22 @@ func Test_xxd_max_cols()
endfor
endfunc
+
+" This used to trigger a buffer overflow (#14738)
+func Test_xxd_buffer_overflow()
+ CheckUnix
+ if system('file ' .. s:xxd_cmd) =~ '32-bit'
+ throw 'Skipped: test only works on 64-bit architecture'
+ endif
+ new
+ let input = repeat('A', 256)
+ call writefile(['-9223372036854775808: ' . repeat("\e[1;32m41\e[0m ", 256) . ' ' . repeat("\e[1;32mA\e[0m", 256)], 'Xxdexpected', 'D')
+ exe 'r! printf ' . input . '| ' . s:xxd_cmd . ' -Ralways -g1 -c256 -d -o 9223372036854775808 > Xxdout'
+ call assert_equalfile('Xxdexpected', 'Xxdout')
+ call delete('Xxdout')
+ bwipe!
+endfunc
+
" -c0 selects the format specific default column value, as if no -c was given
" except for -ps, where it disables extra newlines
func Test_xxd_c0_is_def_cols()
diff --git a/src/testing.c b/src/testing.c
index 33de3a5..3e9e077 100644
--- a/src/testing.c
+++ b/src/testing.c
@@ -1051,6 +1051,8 @@ f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
ml_get_alloc_lines = val;
else if (STRCMP(name, (char_u *)"autoload") == 0)
override_autoload = val;
+ else if (STRCMP(name, (char_u *)"defcompile") == 0)
+ override_defcompile = val;
else if (STRCMP(name, (char_u *)"ALL") == 0)
{
disable_char_avail_for_testing = FALSE;
diff --git a/src/userfunc.c b/src/userfunc.c
index 71b3983..7536234 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -5452,6 +5452,10 @@ define_function(
// :func does not use Vim9 script syntax, even in a Vim9 script file
fp->uf_script_ctx.sc_version = SCRIPT_VERSION_MAX;
+ // If test_override('defcompile' 1) is used, then compile the function now
+ if (eap->cmdidx == CMD_def && override_defcompile)
+ defcompile_function(fp, NULL);
+
goto ret_free;
erret:
diff --git a/src/version.c b/src/version.c
index 71e56a2..14a5922 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,244 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 496,
+/**/
+ 495,
+/**/
+ 494,
+/**/
+ 493,
+/**/
+ 492,
+/**/
+ 491,
+/**/
+ 490,
+/**/
+ 489,
+/**/
+ 488,
+/**/
+ 487,
+/**/
+ 486,
+/**/
+ 485,
+/**/
+ 484,
+/**/
+ 483,
+/**/
+ 482,
+/**/
+ 481,
+/**/
+ 480,
+/**/
+ 479,
+/**/
+ 478,
+/**/
+ 477,
+/**/
+ 476,
+/**/
+ 475,
+/**/
+ 474,
+/**/
+ 473,
+/**/
+ 472,
+/**/
+ 471,
+/**/
+ 470,
+/**/
+ 469,
+/**/
+ 468,
+/**/
+ 467,
+/**/
+ 466,
+/**/
+ 465,
+/**/
+ 464,
+/**/
+ 463,
+/**/
+ 462,
+/**/
+ 461,
+/**/
+ 460,
+/**/
+ 459,
+/**/
+ 458,
+/**/
+ 457,
+/**/
+ 456,
+/**/
+ 455,
+/**/
+ 454,
+/**/
+ 453,
+/**/
+ 452,
+/**/
+ 451,
+/**/
+ 450,
+/**/
+ 449,
+/**/
+ 448,
+/**/
+ 447,
+/**/
+ 446,
+/**/
+ 445,
+/**/
+ 444,
+/**/
+ 443,
+/**/
+ 442,
+/**/
+ 441,
+/**/
+ 440,
+/**/
+ 439,
+/**/
+ 438,
+/**/
+ 437,
+/**/
+ 436,
+/**/
+ 435,
+/**/
+ 434,
+/**/
+ 433,
+/**/
+ 432,
+/**/
+ 431,
+/**/
+ 430,
+/**/
+ 429,
+/**/
+ 428,
+/**/
+ 427,
+/**/
+ 426,
+/**/
+ 425,
+/**/
+ 424,
+/**/
+ 423,
+/**/
+ 422,
+/**/
+ 421,
+/**/
+ 420,
+/**/
+ 419,
+/**/
+ 418,
+/**/
+ 417,
+/**/
+ 416,
+/**/
+ 415,
+/**/
+ 414,
+/**/
+ 413,
+/**/
+ 412,
+/**/
+ 411,
+/**/
+ 410,
+/**/
+ 409,
+/**/
+ 408,
+/**/
+ 407,
+/**/
+ 406,
+/**/
+ 405,
+/**/
+ 404,
+/**/
+ 403,
+/**/
+ 402,
+/**/
+ 401,
+/**/
+ 400,
+/**/
+ 399,
+/**/
+ 398,
+/**/
+ 397,
+/**/
+ 396,
+/**/
+ 395,
+/**/
+ 394,
+/**/
+ 393,
+/**/
+ 392,
+/**/
+ 391,
+/**/
+ 390,
+/**/
+ 389,
+/**/
+ 388,
+/**/
+ 387,
+/**/
+ 386,
+/**/
+ 385,
+/**/
+ 384,
+/**/
+ 383,
+/**/
+ 382,
+/**/
+ 381,
+/**/
+ 380,
+/**/
+ 379,
+/**/
+ 378,
+/**/
377,
/**/
376,
diff --git a/src/vim.h b/src/vim.h
index f359245..e703239 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1500,6 +1500,8 @@ typedef enum
, HLF_SPL // SpellLocal
, HLF_PNI // popup menu normal item
, HLF_PSI // popup menu selected item
+ , HLF_PMNI // popup menu matched text in normal item
+ , HLF_PMSI // popup menu matched text in selected item
, HLF_PNK // popup menu normal item "kind"
, HLF_PSK // popup menu selected item "kind"
, HLF_PNX // popup menu normal item "menu" (extra text)
@@ -1525,11 +1527,20 @@ typedef enum
'n', 'a', 'b', 'N', 'G', 'O', 'r', 's', 'S', 'c', 't', 'v', 'V', \
'w', 'W', 'f', 'F', 'A', 'C', 'D', 'T', '-', '>', \
'B', 'P', 'R', 'L', \
- '+', '=', '[', ']', '{', '}', 'x', 'X', \
+ '+', '=', 'k', '<','[', ']', '{', '}', 'x', 'X', \
'*', '#', '_', '!', '.', 'o', 'q', \
'z', 'Z', 'g'}
/*
+ * Values for behaviour in spell_move_to
+ */
+typedef enum
+{
+ SMT_ALL = 0 // Move to "all" words
+ , SMT_BAD // Move to "bad" words only
+ , SMT_RARE // Move to "rare" words only
+} smt_T;
+/*
* Boolean constants
*/
#ifndef TRUE
diff --git a/src/vim9class.c b/src/vim9class.c
index 4314b52..f66aa68 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -137,7 +137,7 @@ parse_member(
(void)skip_expr_concatenate(&init_arg, &expr_start, &expr_end, &evalarg);
init_arg = skipwhite(init_arg);
- if (*init_arg != NUL)
+ if (*init_arg != NUL && !vim9_comment_start(init_arg))
{
semsg(_(e_trailing_characters_str), init_arg);
return FAIL;
@@ -3845,7 +3845,7 @@ object_len(object_T *obj)
* Return a textual representation of object "obj"
*/
char_u *
-object_string(
+object2string(
object_T *obj,
char_u *numbuf,
int copyID,
diff --git a/src/vim9type.c b/src/vim9type.c
index 775291e..ffdf7fa 100644
--- a/src/vim9type.c
+++ b/src/vim9type.c
@@ -1466,7 +1466,14 @@ parse_type_user_defined(
}
if (give_error && (did_emsg == did_emsg_before))
+ {
+ char_u *p = skip_type(*arg, FALSE);
+ char cc = *p;
+
+ *p = NUL;
semsg(_(e_type_not_recognized_str), *arg);
+ *p = cc;
+ }
return NULL;
}
@@ -2091,10 +2098,12 @@ check_typval_is_value(typval_T *tv)
case VAR_CLASS:
{
class_T *cl = tv->vval.v_class;
- if (IS_ENUM(cl))
- semsg(_(e_using_enum_as_value_str), cl->class_name);
+ char_u *class_name = (cl == NULL) ? (char_u *)""
+ : cl->class_name;
+ if (cl != NULL && IS_ENUM(cl))
+ semsg(_(e_using_enum_as_value_str), class_name);
else
- semsg(_(e_using_class_as_value_str), cl->class_name);
+ semsg(_(e_using_class_as_value_str), class_name);
}
return FAIL;
diff --git a/src/xxd/xxd.c b/src/xxd/xxd.c
index cf8b4ea..7a3d36a 100644
--- a/src/xxd/xxd.c
+++ b/src/xxd/xxd.c
@@ -62,6 +62,7 @@
* 17.01.2024 use size_t instead of usigned int for code-generation (-i), #13876
* 25.01.2024 revert the previous patch (size_t instead of unsigned int)
* 10.02.2024 fix buffer-overflow when writing color output to buffer, #14003
+ * 10.05.2024 fix another buffer-overflow when writing colored output to buffer, #14738
*
* (c) 1990-1998 by Juergen Weigert (jnweiger@gmail.com)
*
@@ -142,7 +143,7 @@ extern void perror __P((char *));
# endif
#endif
-char version[] = "xxd 2024-02-10 by Juergen Weigert et al.";
+char version[] = "xxd 2024-05-10 by Juergen Weigert et al.";
#ifdef WIN32
char osver[] = " (Win32)";
#else
@@ -205,29 +206,16 @@ char osver[] = "";
/*
* LLEN is the maximum length of a line; other than the visible characters
* we need to consider also the escape color sequence prologue/epilogue ,
- * (11 bytes for each character). The most larger format is the default one:
- * addr + 1 word for each col/2 + 1 char for each col
- *
- * addr 1st group 2nd group
- * +-------+ +-----------------+ +------+
- * 01234567: 1234 5678 9abc def0 12345678
- *
- * - addr: typically 012345678: -> from 10 up to 18 bytes (including trailing
- * space)
- * - 1st group: 1234 5678 9abc ... -> each byte may be colored, so add 11
- * for each byte
- * - space -> 1 byte
- * - 2nd group: 12345678 -> each char may be colore so add 11
- * for each byte
- * - new line -> 1 byte
- * - zero (end line) -> 1 byte
+ * (11 bytes for each character).
*/
-#define LLEN (2*(int)sizeof(unsigned long) + 2 + /* addr + ": " */ \
- (11 * 2 + 4 + 1) * (COLS / 2) + /* 1st group */ \
- 1 + /* space */ \
- (1 + 11) * COLS + /* 2nd group */ \
- 1 + /* new line */ \
- 1) /* zero */
+#define LLEN \
+ (39 /* addr: ⌈log10(ULONG_MAX)⌉ if "-d" flag given. We assume ULONG_MAX = 2**128 */ \
+ + 2 /* ": " */ \
+ + 13 * COLS /* hex dump with colors */ \
+ + (COLS - 1) /* whitespace between groups if "-g1" option given and "-c" maxed out */ \
+ + 2 /* whitespace */ \
+ + 12 * COLS /* ASCII dump with colors */ \
+ + 2) /* "\n\0" */
char hexxa[] = "0123456789abcdef0123456789ABCDEF", *hexx = hexxa;