From bee19c22d569e54513a9c591441c7f411811dc81 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 04:10:58 +0200 Subject: Adding upstream version 2:9.1.0374. Signed-off-by: Daniel Baumann --- src/Make_mvc.mak | 55 +- src/Makefile | 10 +- src/auto/configure | 8 +- src/autocmd.c | 422 ++-- src/buffer.c | 6 +- src/change.c | 3 +- src/charset.c | 41 +- src/clientserver.c | 8 +- src/configure.ac | 6 +- src/dict.c | 6 + src/dosinst.c | 2 +- src/drawline.c | 74 +- src/edit.c | 95 +- src/errors.h | 46 +- src/eval.c | 164 +- src/evalfunc.c | 35 +- src/evalvars.c | 141 +- src/ex_cmds.c | 2 +- src/ex_cmds.h | 5 +- src/ex_cmds2.c | 25 +- src/ex_docmd.c | 339 ++- src/fileio.c | 8 +- src/filepath.c | 1 + src/getchar.c | 175 +- src/globals.h | 23 +- src/gui_gtk_x11.c | 3 + src/gui_motif.c | 3 + src/highlight.c | 295 ++- src/indent.c | 4 +- src/insexpand.c | 2 + src/main.c | 6 +- src/map.c | 3 + src/match.c | 4 +- src/mbyte.c | 15 + src/memline.c | 3 + src/message.c | 43 +- src/misc1.c | 17 +- src/misc2.c | 42 + src/mouse.c | 2 +- src/move.c | 900 +++----- src/normal.c | 110 +- src/ops.c | 113 +- src/option.c | 68 +- src/optiondefs.h | 3 +- src/optionstr.c | 2 +- src/os_unix.c | 15 +- src/os_unix.h | 18 + src/po/Make_mvc.mak | 2 + src/po/check.vim | 9 + src/po/ru.cp1251.po | 127 +- src/po/ru.po | 127 +- src/po/sr.po | 88 +- src/popupwin.c | 2 +- src/proto/charset.pro | 1 + src/proto/eval.pro | 6 +- src/proto/evalvars.pro | 1 + src/proto/mbyte.pro | 1 + src/proto/misc2.pro | 4 + src/proto/move.pro | 5 +- src/proto/normal.pro | 3 +- src/proto/scriptfile.pro | 1 + src/proto/vim9class.pro | 1 + src/proto/vim9instr.pro | 2 +- src/proto/window.pro | 1 + src/quickfix.c | 4 +- src/regexp_bt.c | 2 +- src/regexp_nfa.c | 4 +- src/screen.c | 16 +- src/scriptfile.c | 62 +- src/session.c | 2 + src/structs.h | 40 +- src/tag.c | 22 +- src/term.c | 2 + src/termdefs.h | 6 +- src/testdir/Make_all.mak | 5 + src/testdir/Makefile | 2 +- src/testdir/dumps/Test_ambiwidth_hl_dump_1.dump | 6 + src/testdir/dumps/Test_ambiwidth_hl_dump_2.dump | 6 + .../dumps/Test_display_visual_block_scroll.dump | 10 +- ..._normal_gj_gk_gM_with_outer_virtual_text_1.dump | 16 + ..._normal_gj_gk_gM_with_outer_virtual_text_2.dump | 16 + ..._normal_gj_gk_gM_with_outer_virtual_text_3.dump | 16 + ..._normal_gj_gk_gM_with_outer_virtual_text_4.dump | 16 + ...ps_with_text_truncated_just_before_after_1.dump | 8 + ...ps_with_text_truncated_just_before_after_2.dump | 8 + ...widths_with_non_ambiwidth_character_dump_1.dump | 6 + ...widths_with_non_ambiwidth_character_dump_2.dump | 6 + src/testdir/dumps/Test_smooth_long_6.dump | 2 +- src/testdir/dumps/Test_smooth_long_7.dump | 2 +- src/testdir/test_autocmd.vim | 351 ++- src/testdir/test_buffer.vim | 23 +- src/testdir/test_cmdline.vim | 23 +- src/testdir/test_cmdmods.vim | 6 +- src/testdir/test_codestyle.vim | 8 +- src/testdir/test_compiler.vim | 4 +- src/testdir/test_conceal.vim | 1 + src/testdir/test_diffmode.vim | 62 +- src/testdir/test_digraph.vim | 4 +- src/testdir/test_edit.vim | 156 +- src/testdir/test_expr.vim | 16 + src/testdir/test_filechanged.vim | 34 +- src/testdir/test_filetype.vim | 106 +- src/testdir/test_functions.vim | 37 + src/testdir/test_highlight.vim | 9 + src/testdir/test_history.vim | 2 +- src/testdir/test_ins_complete.vim | 24 +- src/testdir/test_let.vim | 132 ++ src/testdir/test_listdict.vim | 6 + src/testdir/test_mapping.vim | 67 +- src/testdir/test_match.vim | 2 +- src/testdir/test_mksession.vim | 4 +- src/testdir/test_normal.vim | 93 +- src/testdir/test_put.vim | 17 + src/testdir/test_recover.vim | 2 +- src/testdir/test_regexp_latin.vim | 14 + src/testdir/test_regexp_utf8.vim | 2 +- src/testdir/test_registers.vim | 69 +- src/testdir/test_remote.vim | 45 + src/testdir/test_scroll_opt.vim | 116 +- src/testdir/test_sound.vim | 2 + src/testdir/test_tabpage.vim | 8 +- src/testdir/test_taglist.vim | 5 +- src/testdir/test_terminal3.vim | 20 + src/testdir/test_textformat.vim | 9 + src/testdir/test_textprop.vim | 79 + src/testdir/test_utf8.vim | 121 +- src/testdir/test_vim9_assign.vim | 106 + src/testdir/test_vim9_builtin.vim | 2232 ++++++++++---------- src/testdir/test_vim9_class.vim | 236 ++- src/testdir/test_vim9_disassemble.vim | 30 +- src/testdir/test_vim9_enum.vim | 1537 ++++++++++++++ src/testdir/test_vim9_func.vim | 31 + src/testdir/test_vim9_import.vim | 243 +++ src/testdir/test_vim9_script.vim | 130 +- src/testdir/test_visual.vim | 2 +- src/testdir/test_winfixbuf.vim | 415 ++-- src/testdir/test_xdg.vim | 295 +++ src/testdir/vim9.vim | 208 +- src/testing.c | 11 +- src/textformat.c | 6 +- src/textprop.c | 8 +- src/typval.c | 16 +- src/userfunc.c | 53 +- src/version.c | 363 +++- src/version.h | 17 +- src/vim.h | 17 +- src/vim9.h | 10 +- src/vim9class.c | 550 ++++- src/vim9cmds.c | 4 +- src/vim9compile.c | 758 ++++--- src/vim9execute.c | 33 +- src/vim9expr.c | 21 +- src/vim9instr.c | 23 +- src/vim9script.c | 25 +- src/vim9type.c | 1332 +++++++----- src/window.c | 10 +- 156 files changed, 10390 insertions(+), 4048 deletions(-) create mode 100644 src/testdir/dumps/Test_ambiwidth_hl_dump_1.dump create mode 100644 src/testdir/dumps/Test_ambiwidth_hl_dump_2.dump create mode 100644 src/testdir/dumps/Test_prop_normal_gj_gk_gM_with_outer_virtual_text_1.dump create mode 100644 src/testdir/dumps/Test_prop_normal_gj_gk_gM_with_outer_virtual_text_2.dump create mode 100644 src/testdir/dumps/Test_prop_normal_gj_gk_gM_with_outer_virtual_text_3.dump create mode 100644 src/testdir/dumps/Test_prop_normal_gj_gk_gM_with_outer_virtual_text_4.dump create mode 100644 src/testdir/dumps/Test_props_with_text_truncated_just_before_after_1.dump create mode 100644 src/testdir/dumps/Test_props_with_text_truncated_just_before_after_2.dump create mode 100644 src/testdir/dumps/Test_setcellwidths_with_non_ambiwidth_character_dump_1.dump create mode 100644 src/testdir/dumps/Test_setcellwidths_with_non_ambiwidth_character_dump_2.dump create mode 100644 src/testdir/test_vim9_enum.vim create mode 100644 src/testdir/test_xdg.vim (limited to 'src') diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 4db2298..4d03a72 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -157,10 +157,55 @@ # you can set DEFINES on the command line, e.g., # nmake -f Make_mvc.mvc "DEFINES=-DEMACS_TAGS" +RM= del /f /q +PS= powershell.exe + +PSFLAGS= -NoLogo -NoProfile -Command + +!IF ![$(PS) $(PSFLAGS) try{Out-File -FilePath '.\major.tmp' -InputObject \ + \"MAJOR=$$(((Select-String -Pattern 'VIM_VERSION_MAJOR\s+\d{1,2}' \ + -Path '.\version.h').Line[-2..-1^]-join '').Trim())\"} \ + catch{exit 1}] +! INCLUDE .\major.tmp +! IF [$(RM) .\major.tmp] +! ENDIF +!ELSE +# Change this value for the new version +MAJOR= 9 +!ENDIF + +!IF ![$(PS) $(PSFLAGS) try{Out-File -FilePath '.\minor.tmp' -InputObject \ + \"MINOR=$$(((Select-String -Pattern 'VIM_VERSION_MINOR\s+\d{1,2}' \ + -Path '.\version.h').Line[-2..-1^]-join '').Trim())\"} \ + catch{exit 1}] +! INCLUDE .\minor.tmp +! IF [$(RM) .\minor.tmp] +! ENDIF +!ELSE +# Change this value for the new version +MINOR= 1 +!ENDIF + +!IF ![$(PS) $(PSFLAGS) try{Out-File -FilePath '.\patchlvl.tmp' -InputObject \ + \"PATCHLEVEL=$$([decimal^]((Get-Content -Path '.\version.c' \ + -TotalCount ((Select-String -Pattern 'static int included_patches' \ + -Path '.\version.c').LineNumber+3))[-1^]).Trim().TrimEnd(','))\"} \ + catch{exit 1}] +! INCLUDE .\patchlvl.tmp +! IF [$(RM) .\patchlvl.tmp] +! ENDIF +!ENDIF + + # Build on Windows NT/XP TARGETOS = WINNT +!IFDEF PATCHLEVEL +RCFLAGS= -DVIM_VERSION_PATCHLEVEL=$(PATCHLEVEL) +!ENDIF + + !if "$(VIMDLL)" == "yes" GUI = yes !endif @@ -591,7 +636,7 @@ OPTFLAG = $(OPTFLAG) /GL ! endif CFLAGS = $(CFLAGS) $(OPTFLAG) -DNDEBUG $(CPUARG) -RCFLAGS = -DNDEBUG +RCFLAGS = $(RCFLAGS) -DNDEBUG ! ifdef USE_MSVCRT CFLAGS = $(CFLAGS) /MD LIBC = msvcrt.lib @@ -607,7 +652,7 @@ VIM = vimd DEBUGINFO = /ZI ! endif CFLAGS = $(CFLAGS) -D_DEBUG -DDEBUG /Od -RCFLAGS = -D_DEBUG -DDEBUG +RCFLAGS = $(RCFLAGS) -D_DEBUG -DDEBUG # The /fixed:no is needed for Quantify. LIBC = /fixed:no ! ifdef USE_MSVCRT @@ -621,7 +666,7 @@ LIBC = $(LIBC) libcmtd.lib !endif # DEBUG # Visual Studio 2005 has 'deprecated' many of the standard CRT functions -CFLAGS_DEPR = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE +CFLAGS_DEPR = -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE CFLAGS = $(CFLAGS) $(CFLAGS_DEPR) !include Make_all.mak @@ -1276,6 +1321,10 @@ $(OUTDIR): CFLAGS_INST = /nologo /O2 -DNDEBUG -DWIN32 -DWINVER=$(WINVER) -D_WIN32_WINNT=$(WINVER) $(CFLAGS_DEPR) +!IFDEF PATCHLEVEL +CFLAGS_INST= $(CFLAGS_INST) -DVIM_VERSION_PATCHLEVEL=$(PATCHLEVEL) +!ENDIF + install.exe: dosinst.c dosinst.h version.h $(CC) $(CFLAGS_INST) dosinst.c kernel32.lib shell32.lib \ user32.lib ole32.lib advapi32.lib uuid.lib \ diff --git a/src/Makefile b/src/Makefile index 33903d3..29f63dc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2341,6 +2341,7 @@ installrtbase: $(HELPSOURCE)/vim.1 $(DEST_VIM) $(VIMTARGET) $(DEST_RT) \ $(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)/rust $(DEST_AUTO)/cargo \ $(DEST_IMPORT) $(DEST_IMPORT)/dist \ $(DEST_PLUG) $(DEST_TUTOR) $(DEST_SPELL) $(DEST_COMP) -$(SHELL) ./installman.sh install $(DEST_MAN) "" $(INSTALLMANARGS) @@ -2428,6 +2429,10 @@ installrtbase: $(HELPSOURCE)/vim.1 $(DEST_VIM) $(VIMTARGET) $(DEST_RT) \ 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 + cd $(DEST_AUTO)/rust; chmod $(HELPMOD) *.vim # install the standard import files cd $(IMPORTSOURCE)/dist; $(INSTALL_DATA) *.vim $(DEST_IMPORT)/dist cd $(DEST_IMPORT)/dist; chmod $(HELPMOD) *.vim @@ -2669,6 +2674,7 @@ $(DESTDIR)$(exec_prefix) $(DEST_BIN) \ $(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)/cargo $(DEST_AUTO)/rust \ $(DEST_IMPORT) $(DEST_IMPORT)/dist $(DEST_PLUG): $(MKDIR_P) $@ -chmod $(DIRMOD) $@ @@ -2859,10 +2865,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 + -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_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) + -rmdir $(DEST_FTP) $(DEST_AUTO)/dist $(DEST_AUTO)/xml $(DEST_AUTO)/zig $(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. diff --git a/src/auto/configure b/src/auto/configure index 9712104..0e0cf8e 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -15965,16 +15965,16 @@ printf "%s\n" "yes" >&6; } printf "%s\n" "no" >&6; } fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if msgfmt supports --no-convert" >&5 -printf %s "checking if msgfmt supports --no-convert... " >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if $MSGFMT supports --no-convert" >&5 +printf %s "checking if $MSGFMT supports --no-convert... " >&6; } if "$MSGFMT" --help | grep -q -- '--no-convert' >/dev/null; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - MSGFMTCMD="OLD_PO_FILE_INPUT=yes msgfmt --no-convert -v" + MSGFMTCMD="OLD_PO_FILE_INPUT=yes $MSGFMT --no-convert -v" else { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 printf "%s\n" "no" >&6; } - MSGFMTCMD="OLD_PO_FILE_INPUT=yes msgfmt -v" + MSGFMTCMD="OLD_PO_FILE_INPUT=yes $MSGFMT -v" fi fi diff --git a/src/autocmd.c b/src/autocmd.c index 8e43b34..bce57cb 100644 --- a/src/autocmd.c +++ b/src/autocmd.c @@ -74,152 +74,166 @@ typedef struct AutoPat char last; // last pattern for apply_autocmds() } AutoPat; -static struct event_name -{ - char *name; // event name - event_T event; // event number -} event_names[] = -{ - {"BufAdd", EVENT_BUFADD}, - {"BufCreate", EVENT_BUFADD}, - {"BufDelete", EVENT_BUFDELETE}, - {"BufEnter", EVENT_BUFENTER}, - {"BufFilePost", EVENT_BUFFILEPOST}, - {"BufFilePre", EVENT_BUFFILEPRE}, - {"BufHidden", EVENT_BUFHIDDEN}, - {"BufLeave", EVENT_BUFLEAVE}, - {"BufNew", EVENT_BUFNEW}, - {"BufNewFile", EVENT_BUFNEWFILE}, - {"BufRead", EVENT_BUFREADPOST}, - {"BufReadCmd", EVENT_BUFREADCMD}, - {"BufReadPost", EVENT_BUFREADPOST}, - {"BufReadPre", EVENT_BUFREADPRE}, - {"BufUnload", EVENT_BUFUNLOAD}, - {"BufWinEnter", EVENT_BUFWINENTER}, - {"BufWinLeave", EVENT_BUFWINLEAVE}, - {"BufWipeout", EVENT_BUFWIPEOUT}, - {"BufWrite", EVENT_BUFWRITEPRE}, - {"BufWritePost", EVENT_BUFWRITEPOST}, - {"BufWritePre", EVENT_BUFWRITEPRE}, - {"BufWriteCmd", EVENT_BUFWRITECMD}, - {"CmdlineChanged", EVENT_CMDLINECHANGED}, - {"CmdlineEnter", EVENT_CMDLINEENTER}, - {"CmdlineLeave", EVENT_CMDLINELEAVE}, - {"CmdwinEnter", EVENT_CMDWINENTER}, - {"CmdwinLeave", EVENT_CMDWINLEAVE}, - {"CmdUndefined", EVENT_CMDUNDEFINED}, - {"ColorScheme", EVENT_COLORSCHEME}, - {"ColorSchemePre", EVENT_COLORSCHEMEPRE}, - {"CompleteChanged", EVENT_COMPLETECHANGED}, - {"CompleteDone", EVENT_COMPLETEDONE}, - {"CompleteDonePre", EVENT_COMPLETEDONEPRE}, - {"CursorHold", EVENT_CURSORHOLD}, - {"CursorHoldI", EVENT_CURSORHOLDI}, - {"CursorMoved", EVENT_CURSORMOVED}, - {"CursorMovedI", EVENT_CURSORMOVEDI}, - {"DiffUpdated", EVENT_DIFFUPDATED}, - {"DirChanged", EVENT_DIRCHANGED}, - {"DirChangedPre", EVENT_DIRCHANGEDPRE}, - {"EncodingChanged", EVENT_ENCODINGCHANGED}, - {"ExitPre", EVENT_EXITPRE}, - {"FileEncoding", EVENT_ENCODINGCHANGED}, - {"FileAppendPost", EVENT_FILEAPPENDPOST}, - {"FileAppendPre", EVENT_FILEAPPENDPRE}, - {"FileAppendCmd", EVENT_FILEAPPENDCMD}, - {"FileChangedShell",EVENT_FILECHANGEDSHELL}, - {"FileChangedShellPost",EVENT_FILECHANGEDSHELLPOST}, - {"FileChangedRO", EVENT_FILECHANGEDRO}, - {"FileReadPost", EVENT_FILEREADPOST}, - {"FileReadPre", EVENT_FILEREADPRE}, - {"FileReadCmd", EVENT_FILEREADCMD}, - {"FileType", EVENT_FILETYPE}, - {"FileWritePost", EVENT_FILEWRITEPOST}, - {"FileWritePre", EVENT_FILEWRITEPRE}, - {"FileWriteCmd", EVENT_FILEWRITECMD}, - {"FilterReadPost", EVENT_FILTERREADPOST}, - {"FilterReadPre", EVENT_FILTERREADPRE}, - {"FilterWritePost", EVENT_FILTERWRITEPOST}, - {"FilterWritePre", EVENT_FILTERWRITEPRE}, - {"FocusGained", EVENT_FOCUSGAINED}, - {"FocusLost", EVENT_FOCUSLOST}, - {"FuncUndefined", EVENT_FUNCUNDEFINED}, - {"GUIEnter", EVENT_GUIENTER}, - {"GUIFailed", EVENT_GUIFAILED}, - {"InsertChange", EVENT_INSERTCHANGE}, - {"InsertEnter", EVENT_INSERTENTER}, - {"InsertLeave", EVENT_INSERTLEAVE}, - {"InsertLeavePre", EVENT_INSERTLEAVEPRE}, - {"InsertCharPre", EVENT_INSERTCHARPRE}, - {"MenuPopup", EVENT_MENUPOPUP}, - {"ModeChanged", EVENT_MODECHANGED}, - {"OptionSet", EVENT_OPTIONSET}, - {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST}, - {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE}, - {"QuitPre", EVENT_QUITPRE}, - {"RemoteReply", EVENT_REMOTEREPLY}, - {"SafeState", EVENT_SAFESTATE}, - {"SafeStateAgain", EVENT_SAFESTATEAGAIN}, - {"SessionLoadPost", EVENT_SESSIONLOADPOST}, - {"ShellCmdPost", EVENT_SHELLCMDPOST}, - {"ShellFilterPost", EVENT_SHELLFILTERPOST}, - {"SigUSR1", EVENT_SIGUSR1}, - {"SourceCmd", EVENT_SOURCECMD}, - {"SourcePre", EVENT_SOURCEPRE}, - {"SourcePost", EVENT_SOURCEPOST}, - {"SpellFileMissing",EVENT_SPELLFILEMISSING}, - {"StdinReadPost", EVENT_STDINREADPOST}, - {"StdinReadPre", EVENT_STDINREADPRE}, - {"SwapExists", EVENT_SWAPEXISTS}, - {"Syntax", EVENT_SYNTAX}, - {"TabNew", EVENT_TABNEW}, - {"TabClosed", EVENT_TABCLOSED}, - {"TabEnter", EVENT_TABENTER}, - {"TabLeave", EVENT_TABLEAVE}, - {"TermChanged", EVENT_TERMCHANGED}, - {"TerminalOpen", EVENT_TERMINALOPEN}, - {"TerminalWinOpen", EVENT_TERMINALWINOPEN}, - {"TermResponse", EVENT_TERMRESPONSE}, - {"TermResponseAll", EVENT_TERMRESPONSEALL}, - {"TextChanged", EVENT_TEXTCHANGED}, - {"TextChangedI", EVENT_TEXTCHANGEDI}, - {"TextChangedP", EVENT_TEXTCHANGEDP}, - {"TextChangedT", EVENT_TEXTCHANGEDT}, - {"User", EVENT_USER}, - {"VimEnter", EVENT_VIMENTER}, - {"VimLeave", EVENT_VIMLEAVE}, - {"VimLeavePre", EVENT_VIMLEAVEPRE}, - {"WinNewPre", EVENT_WINNEWPRE}, - {"WinNew", EVENT_WINNEW}, - {"WinClosed", EVENT_WINCLOSED}, - {"WinEnter", EVENT_WINENTER}, - {"WinLeave", EVENT_WINLEAVE}, - {"WinResized", EVENT_WINRESIZED}, - {"WinScrolled", EVENT_WINSCROLLED}, - {"VimResized", EVENT_VIMRESIZED}, - {"TextYankPost", EVENT_TEXTYANKPOST}, - {"VimSuspend", EVENT_VIMSUSPEND}, - {"VimResume", EVENT_VIMRESUME}, - {NULL, (event_T)0} +// +// special cases: +// BufNewFile and BufRead are searched for ALOT (especially at startup) +// so we pre-determine their index into the event_tab[] table for fast access. +// Keep these values in sync with event_tab[]! +#define BUFNEWFILE_INDEX 9 +#define BUFREAD_INDEX 10 + +// must be sorted by the 'value' field because it is used by bsearch()! +static keyvalue_T event_tab[] = { + KEYVALUE_ENTRY(EVENT_BUFADD, "BufAdd"), + KEYVALUE_ENTRY(EVENT_BUFADD, "BufCreate"), + KEYVALUE_ENTRY(EVENT_BUFDELETE, "BufDelete"), + KEYVALUE_ENTRY(EVENT_BUFENTER, "BufEnter"), + KEYVALUE_ENTRY(EVENT_BUFFILEPOST, "BufFilePost"), + KEYVALUE_ENTRY(EVENT_BUFFILEPRE, "BufFilePre"), + KEYVALUE_ENTRY(EVENT_BUFHIDDEN, "BufHidden"), + KEYVALUE_ENTRY(EVENT_BUFLEAVE, "BufLeave"), + KEYVALUE_ENTRY(EVENT_BUFNEW, "BufNew"), + KEYVALUE_ENTRY(EVENT_BUFNEWFILE, "BufNewFile"), // BUFNEWFILE_INDEX + KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufRead"), // BUFREAD_INDEX + KEYVALUE_ENTRY(EVENT_BUFREADCMD, "BufReadCmd"), + KEYVALUE_ENTRY(EVENT_BUFREADPOST, "BufReadPost"), + KEYVALUE_ENTRY(EVENT_BUFREADPRE, "BufReadPre"), + KEYVALUE_ENTRY(EVENT_BUFUNLOAD, "BufUnload"), + KEYVALUE_ENTRY(EVENT_BUFWINENTER, "BufWinEnter"), + KEYVALUE_ENTRY(EVENT_BUFWINLEAVE, "BufWinLeave"), + KEYVALUE_ENTRY(EVENT_BUFWIPEOUT, "BufWipeout"), + KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWrite"), + KEYVALUE_ENTRY(EVENT_BUFWRITECMD, "BufWriteCmd"), + KEYVALUE_ENTRY(EVENT_BUFWRITEPOST, "BufWritePost"), + KEYVALUE_ENTRY(EVENT_BUFWRITEPRE, "BufWritePre"), + KEYVALUE_ENTRY(EVENT_CMDLINECHANGED, "CmdlineChanged"), + KEYVALUE_ENTRY(EVENT_CMDLINEENTER, "CmdlineEnter"), + KEYVALUE_ENTRY(EVENT_CMDLINELEAVE, "CmdlineLeave"), + KEYVALUE_ENTRY(EVENT_CMDUNDEFINED, "CmdUndefined"), + KEYVALUE_ENTRY(EVENT_CMDWINENTER, "CmdwinEnter"), + KEYVALUE_ENTRY(EVENT_CMDWINLEAVE, "CmdwinLeave"), + KEYVALUE_ENTRY(EVENT_COLORSCHEME, "ColorScheme"), + KEYVALUE_ENTRY(EVENT_COLORSCHEMEPRE, "ColorSchemePre"), + KEYVALUE_ENTRY(EVENT_COMPLETECHANGED, "CompleteChanged"), + KEYVALUE_ENTRY(EVENT_COMPLETEDONE, "CompleteDone"), + KEYVALUE_ENTRY(EVENT_COMPLETEDONEPRE, "CompleteDonePre"), + KEYVALUE_ENTRY(EVENT_CURSORHOLD, "CursorHold"), + KEYVALUE_ENTRY(EVENT_CURSORHOLDI, "CursorHoldI"), + KEYVALUE_ENTRY(EVENT_CURSORMOVED, "CursorMoved"), + KEYVALUE_ENTRY(EVENT_CURSORMOVEDI, "CursorMovedI"), + KEYVALUE_ENTRY(EVENT_DIFFUPDATED, "DiffUpdated"), + KEYVALUE_ENTRY(EVENT_DIRCHANGED, "DirChanged"), + KEYVALUE_ENTRY(EVENT_DIRCHANGEDPRE, "DirChangedPre"), + KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "EncodingChanged"), + KEYVALUE_ENTRY(EVENT_EXITPRE, "ExitPre"), + KEYVALUE_ENTRY(EVENT_FILEAPPENDCMD, "FileAppendCmd"), + KEYVALUE_ENTRY(EVENT_FILEAPPENDPOST, "FileAppendPost"), + KEYVALUE_ENTRY(EVENT_FILEAPPENDPRE, "FileAppendPre"), + KEYVALUE_ENTRY(EVENT_FILECHANGEDRO, "FileChangedRO"), + KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELL, "FileChangedShell"), + KEYVALUE_ENTRY(EVENT_FILECHANGEDSHELLPOST, "FileChangedShellPost"), + KEYVALUE_ENTRY(EVENT_ENCODINGCHANGED, "FileEncoding"), + KEYVALUE_ENTRY(EVENT_FILEREADCMD, "FileReadCmd"), + KEYVALUE_ENTRY(EVENT_FILEREADPOST, "FileReadPost"), + KEYVALUE_ENTRY(EVENT_FILEREADPRE, "FileReadPre"), + KEYVALUE_ENTRY(EVENT_FILETYPE, "FileType"), + KEYVALUE_ENTRY(EVENT_FILEWRITECMD, "FileWriteCmd"), + KEYVALUE_ENTRY(EVENT_FILEWRITEPOST, "FileWritePost"), + KEYVALUE_ENTRY(EVENT_FILEWRITEPRE, "FileWritePre"), + KEYVALUE_ENTRY(EVENT_FILTERREADPOST, "FilterReadPost"), + KEYVALUE_ENTRY(EVENT_FILTERREADPRE, "FilterReadPre"), + KEYVALUE_ENTRY(EVENT_FILTERWRITEPOST, "FilterWritePost"), + KEYVALUE_ENTRY(EVENT_FILTERWRITEPRE, "FilterWritePre"), + KEYVALUE_ENTRY(EVENT_FOCUSGAINED, "FocusGained"), + KEYVALUE_ENTRY(EVENT_FOCUSLOST, "FocusLost"), + KEYVALUE_ENTRY(EVENT_FUNCUNDEFINED, "FuncUndefined"), + KEYVALUE_ENTRY(EVENT_GUIENTER, "GUIEnter"), + KEYVALUE_ENTRY(EVENT_GUIFAILED, "GUIFailed"), + KEYVALUE_ENTRY(EVENT_INSERTCHANGE, "InsertChange"), + KEYVALUE_ENTRY(EVENT_INSERTCHARPRE, "InsertCharPre"), + KEYVALUE_ENTRY(EVENT_INSERTENTER, "InsertEnter"), + KEYVALUE_ENTRY(EVENT_INSERTLEAVE, "InsertLeave"), + KEYVALUE_ENTRY(EVENT_INSERTLEAVEPRE, "InsertLeavePre"), + KEYVALUE_ENTRY(EVENT_MENUPOPUP, "MenuPopup"), + KEYVALUE_ENTRY(EVENT_MODECHANGED, "ModeChanged"), + KEYVALUE_ENTRY(EVENT_OPTIONSET, "OptionSet"), + KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPOST, "QuickFixCmdPost"), + KEYVALUE_ENTRY(EVENT_QUICKFIXCMDPRE, "QuickFixCmdPre"), + KEYVALUE_ENTRY(EVENT_QUITPRE, "QuitPre"), + KEYVALUE_ENTRY(EVENT_REMOTEREPLY, "RemoteReply"), + KEYVALUE_ENTRY(EVENT_SAFESTATE, "SafeState"), + KEYVALUE_ENTRY(EVENT_SAFESTATEAGAIN, "SafeStateAgain"), + KEYVALUE_ENTRY(EVENT_SESSIONLOADPOST, "SessionLoadPost"), + KEYVALUE_ENTRY(EVENT_SESSIONWRITEPOST, "SessionWritePost"), + KEYVALUE_ENTRY(EVENT_SHELLCMDPOST, "ShellCmdPost"), + KEYVALUE_ENTRY(EVENT_SHELLFILTERPOST, "ShellFilterPost"), + KEYVALUE_ENTRY(EVENT_SIGUSR1, "SigUSR1"), + KEYVALUE_ENTRY(EVENT_SOURCECMD, "SourceCmd"), + KEYVALUE_ENTRY(EVENT_SOURCEPOST, "SourcePost"), + KEYVALUE_ENTRY(EVENT_SOURCEPRE, "SourcePre"), + KEYVALUE_ENTRY(EVENT_SPELLFILEMISSING, "SpellFileMissing"), + KEYVALUE_ENTRY(EVENT_STDINREADPOST, "StdinReadPost"), + KEYVALUE_ENTRY(EVENT_STDINREADPRE, "StdinReadPre"), + KEYVALUE_ENTRY(EVENT_SWAPEXISTS, "SwapExists"), + KEYVALUE_ENTRY(EVENT_SYNTAX, "Syntax"), + KEYVALUE_ENTRY(EVENT_TABCLOSED, "TabClosed"), + KEYVALUE_ENTRY(EVENT_TABENTER, "TabEnter"), + KEYVALUE_ENTRY(EVENT_TABLEAVE, "TabLeave"), + KEYVALUE_ENTRY(EVENT_TABNEW, "TabNew"), + KEYVALUE_ENTRY(EVENT_TERMCHANGED, "TermChanged"), + KEYVALUE_ENTRY(EVENT_TERMINALOPEN, "TerminalOpen"), + KEYVALUE_ENTRY(EVENT_TERMINALWINOPEN, "TerminalWinOpen"), + KEYVALUE_ENTRY(EVENT_TERMRESPONSE, "TermResponse"), + KEYVALUE_ENTRY(EVENT_TERMRESPONSEALL, "TermResponseAll"), + KEYVALUE_ENTRY(EVENT_TEXTCHANGED, "TextChanged"), + KEYVALUE_ENTRY(EVENT_TEXTCHANGEDI, "TextChangedI"), + KEYVALUE_ENTRY(EVENT_TEXTCHANGEDP, "TextChangedP"), + KEYVALUE_ENTRY(EVENT_TEXTCHANGEDT, "TextChangedT"), + KEYVALUE_ENTRY(EVENT_TEXTYANKPOST, "TextYankPost"), + KEYVALUE_ENTRY(EVENT_USER, "User"), + KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"), + KEYVALUE_ENTRY(EVENT_VIMLEAVE, "VimLeave"), + KEYVALUE_ENTRY(EVENT_VIMLEAVEPRE, "VimLeavePre"), + KEYVALUE_ENTRY(EVENT_VIMRESIZED, "VimResized"), + KEYVALUE_ENTRY(EVENT_VIMRESUME, "VimResume"), + KEYVALUE_ENTRY(EVENT_VIMSUSPEND, "VimSuspend"), + KEYVALUE_ENTRY(EVENT_WINCLOSED, "WinClosed"), + KEYVALUE_ENTRY(EVENT_WINENTER, "WinEnter"), + KEYVALUE_ENTRY(EVENT_WINLEAVE, "WinLeave"), + KEYVALUE_ENTRY(EVENT_WINNEW, "WinNew"), + KEYVALUE_ENTRY(EVENT_WINNEWPRE, "WinNewPre"), + KEYVALUE_ENTRY(EVENT_WINRESIZED, "WinResized"), + KEYVALUE_ENTRY(EVENT_WINSCROLLED, "WinScrolled") }; -static AutoPat *first_autopat[NUM_EVENTS] = -{ +static AutoPat *first_autopat[NUM_EVENTS] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + NULL, NULL, NULL, NULL, NULL, NULL }; -static AutoPat *last_autopat[NUM_EVENTS] = -{ +static AutoPat *last_autopat[NUM_EVENTS] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + NULL, NULL, NULL, NULL, NULL, NULL }; #define AUGROUP_DEFAULT (-1) // default autocmd group @@ -265,6 +279,7 @@ static int current_augroup = AUGROUP_DEFAULT; static int au_need_clean = FALSE; // need to delete marked patterns +static event_T event_name2nr(char_u *start, char_u **end); static char_u *event_nr2name(event_T event); static int au_get_grouparg(char_u **argp); static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group, int flags); @@ -680,24 +695,35 @@ is_aucmd_win(win_T *win) event_name2nr(char_u *start, char_u **end) { char_u *p; - int i; - int len; + keyvalue_T target; + keyvalue_T *entry; + static keyvalue_T *bufnewfile = &event_tab[BUFNEWFILE_INDEX]; + static keyvalue_T *bufread = &event_tab[BUFREAD_INDEX]; // the event name ends with end of line, '|', a blank or a comma for (p = start; *p && !VIM_ISWHITE(*p) && *p != ',' && *p != '|'; ++p) ; - for (i = 0; event_names[i].name != NULL; ++i) - { - len = (int)STRLEN(event_names[i].name); - if (len == p - start && STRNICMP(event_names[i].name, start, len) == 0) - break; - } + + target.key = 0; + target.value = (char *)start; + target.length = (size_t)(p - start); + + // special cases: + // BufNewFile and BufRead are searched for ALOT (especially at startup) + // so we check for them first. + if (cmp_keyvalue_value_ni(&target, bufnewfile) == 0) + entry = bufnewfile; + else + if (cmp_keyvalue_value_ni(&target, bufread) == 0) + entry = bufread; + else + entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni); + if (*p == ',') ++p; *end = p; - if (event_names[i].name == NULL) - return NUM_EVENTS; - return event_names[i].event; + + return (entry == NULL) ? NUM_EVENTS : (event_T)entry->key; } /* @@ -707,11 +733,52 @@ event_name2nr(char_u *start, char_u **end) event_nr2name(event_T event) { int i; +#define CACHE_SIZE 12 + static int cache_tab[CACHE_SIZE]; + static int cache_last_index = -1; + + if (cache_last_index < 0) + { + for (i = 0; i < (int)ARRAY_LENGTH(cache_tab); ++i) + cache_tab[i] = -1; + cache_last_index = ARRAY_LENGTH(cache_tab) - 1; + } - for (i = 0; event_names[i].name != NULL; ++i) - if (event_names[i].event == event) - return (char_u *)event_names[i].name; - return (char_u *)"Unknown"; + // first look in the cache + // the cache is circular. to search it we start at the most recent entry + // and go backwards wrapping around when we get to index 0. + for (i = cache_last_index; cache_tab[i] >= 0; ) + { + if ((event_T)event_tab[cache_tab[i]].key == event) + return (char_u *)event_tab[cache_tab[i]].value; + + if (i == 0) + i = ARRAY_LENGTH(cache_tab) - 1; + else + --i; + + // are we back at the start? + if (i == cache_last_index) + break; + } + + // look in the event table itself + for (i = 0; i < (int)ARRAY_LENGTH(event_tab); ++i) + { + if ((event_T)event_tab[i].key == event) + { + // store the found entry in the next position in the cache, + // wrapping around when we get to the maximum index. + if (cache_last_index == ARRAY_LENGTH(cache_tab) - 1) + cache_last_index = 0; + else + ++cache_last_index; + cache_tab[cache_last_index] = i; + break; + } + } + + return (i == (int)ARRAY_LENGTH(event_tab)) ? (char_u *)"Unknown" : (char_u *)event_tab[i].value; } /* @@ -805,12 +872,14 @@ au_event_disable(char *what) { char_u *new_ei; char_u *save_ei; + size_t p_ei_len; - save_ei = vim_strsave(p_ei); + p_ei_len = STRLEN(p_ei); + save_ei = vim_strnsave(p_ei, p_ei_len); if (save_ei == NULL) return NULL; - new_ei = vim_strnsave(p_ei, STRLEN(p_ei) + STRLEN(what)); + new_ei = vim_strnsave(p_ei, p_ei_len + STRLEN(what)); if (new_ei == NULL) { vim_free(save_ei); @@ -1591,6 +1660,11 @@ aucmd_prepbuf( win_init_popup_win(auc_win, buf); + // Make sure tp_localdir and globaldir are NULL to avoid a + // chdir() in win_enter_ext(). + // win_init_popup_win() has already set w_localdir to NULL. + aco->tp_localdir = curtab->tp_localdir; + curtab->tp_localdir = NULL; aco->globaldir = globaldir; globaldir = NULL; @@ -1704,6 +1778,12 @@ win_found: vars_clear(&awp->w_vars->dv_hashtab); // free all w: variables hash_init(&awp->w_vars->dv_hashtab); // re-use the hashtab #endif + // If :lcd has been used in the autocommand window, correct current + // directory before restoring tp_localdir and globaldir. + if (awp->w_localdir != NULL) + win_fix_current_dir(); + vim_free(curtab->tp_localdir); + curtab->tp_localdir = aco->tp_localdir; vim_free(globaldir); globaldir = aco->globaldir; @@ -2257,7 +2337,7 @@ apply_autocmds_group( saveRedobuff(&save_redo); did_save_redobuff = TRUE; } - did_filetype = keep_filetype; + curbuf->b_did_filetype = curbuf->b_keep_filetype; } /* @@ -2269,7 +2349,7 @@ apply_autocmds_group( // Remember that FileType was triggered. Used for did_filetype(). if (event == EVENT_FILETYPE) - did_filetype = TRUE; + curbuf->b_did_filetype = TRUE; tail = gettail(fname); @@ -2378,7 +2458,7 @@ apply_autocmds_group( restore_search_patterns(); if (did_save_redobuff) restoreRedobuff(&save_redo); - did_filetype = FALSE; + curbuf->b_did_filetype = FALSE; while (au_pending_free_buf != NULL) { buf_T *b = au_pending_free_buf->b_next; @@ -2420,7 +2500,7 @@ BYPASS_AU: aubuflocal_remove(buf); if (retval == OK && event == EVENT_FILETYPE) - au_did_filetype = TRUE; + curbuf->b_au_did_filetype = TRUE; return retval; } @@ -2773,6 +2853,8 @@ set_context_in_autocmd( char_u * get_event_name(expand_T *xp UNUSED, int idx) { + int i; + if (idx < augroups.ga_len) // First list group names, if wanted { if (!include_groups || AUGROUP_NAME(idx) == NULL @@ -2780,7 +2862,12 @@ get_event_name(expand_T *xp UNUSED, int idx) return (char_u *)""; // skip deleted entries return AUGROUP_NAME(idx); // return a name } - return (char_u *)event_names[idx - augroups.ga_len].name; + + i = idx - augroups.ga_len; + if (i < 0 || i >= (int)ARRAY_LENGTH(event_tab)) + return NULL; + + return (char_u *)event_tab[i].value; } /* @@ -2790,7 +2877,10 @@ get_event_name(expand_T *xp UNUSED, int idx) char_u * get_event_name_no_group(expand_T *xp UNUSED, int idx) { - return (char_u *)event_names[idx].name; + if (idx < 0 || idx >= (int)ARRAY_LENGTH(event_tab)) + return NULL; + + return (char_u *)event_tab[idx].value; } @@ -3250,8 +3340,6 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) // return only the autocmds for the specified event if (dict_has_key(argvars[0].vval.v_dict, "event")) { - int i; - name = dict_get_string(argvars[0].vval.v_dict, "event", TRUE); if (name == NULL) return; @@ -3260,16 +3348,20 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) event_arg = NUM_EVENTS; else { - for (i = 0; event_names[i].name != NULL; i++) - if (STRICMP(event_names[i].name, name) == 0) - break; - if (event_names[i].name == NULL) + keyvalue_T target; + keyvalue_T *entry; + + target.key = 0; + target.value = (char *)name; + target.length = (int)STRLEN(target.value); + entry = (keyvalue_T *)bsearch(&target, &event_tab, ARRAY_LENGTH(event_tab), sizeof(event_tab[0]), cmp_keyvalue_value_ni); + if (entry == NULL) { semsg(_(e_no_such_event_str), name); vim_free(name); return; } - event_arg = event_names[i].event; + event_arg = (event_T)entry->key; } vim_free(name); } @@ -3314,7 +3406,10 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) event_dict = dict_alloc(); if (event_dict == NULL || list_append_dict(event_list, event_dict) == FAIL) + { + vim_free(pat); return; + } if (dict_add_string(event_dict, "event", event_name) == FAIL || dict_add_string(event_dict, "group", @@ -3329,7 +3424,10 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) || dict_add_bool(event_dict, "once", ac->once) == FAIL || dict_add_bool(event_dict, "nested", ac->nested) == FAIL) + { + vim_free(pat); return; + } } } } diff --git a/src/buffer.c b/src/buffer.c index 243593a..58e9718 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -225,7 +225,7 @@ open_buffer( // The autocommands in readfile() may change the buffer, but only AFTER // reading the file. set_bufref(&old_curbuf, curbuf); - modified_was_set = FALSE; + curbuf->b_modified_was_set = FALSE; // mark cursor position as being invalid curwin->w_valid = 0; @@ -322,7 +322,7 @@ open_buffer( // the changed flag. Unless in readonly mode: "ls | gview -". // When interrupted and 'cpoptions' contains 'i' set changed flag. if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - || modified_was_set // ":set modified" used in autocmd + || curbuf->b_modified_was_set // autocmd did ":set modified" #ifdef FEAT_EVAL || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL) #endif @@ -1944,7 +1944,7 @@ enter_buffer(buf_T *buf) // ":ball" used in an autocommand. If there already is a filetype we // might prefer to keep it. if (*curbuf->b_p_ft == NUL) - did_filetype = FALSE; + curbuf->b_did_filetype = FALSE; open_buffer(FALSE, NULL, 0); } diff --git a/src/change.c b/src/change.c index daf4fae..dacc06f 100644 --- a/src/change.c +++ b/src/change.c @@ -574,8 +574,7 @@ changed_common( && wp->w_topline < lnume && win_linetabsize(wp, wp->w_topline, ml_get(wp->w_topline), (colnr_T)MAXCOL) - <= wp->w_skipcol + sms_marker_overlap(wp, - win_col_off(wp) - win_col_off2(wp))))) + <= wp->w_skipcol + sms_marker_overlap(wp, -1)))) wp->w_skipcol = 0; // Check if a change in the buffer has invalidated the cached diff --git a/src/charset.c b/src/charset.c index 5ae90da..470698f 100644 --- a/src/charset.c +++ b/src/charset.c @@ -798,6 +798,45 @@ linetabsize(win_T *wp, linenr_T lnum) ml_get_buf(wp->w_buffer, lnum, FALSE), (colnr_T)MAXCOL); } +/* + * Like linetabsize(), but excludes 'above'/'after'/'right'/'below' aligned + * virtual text, while keeping inline virtual text. + */ + int +linetabsize_no_outer(win_T *wp, linenr_T lnum) +{ +#ifndef FEAT_PROP_POPUP + return linetabsize(wp, lnum); +#else + chartabsize_T cts; + char_u *line = ml_get_buf(wp->w_buffer, lnum, FALSE); + + init_chartabsize_arg(&cts, wp, lnum, 0, line, line); + + if (cts.cts_text_prop_count) + { + int write_idx = 0; + for (int read_idx = 0; read_idx < cts.cts_text_prop_count; read_idx++) + { + textprop_T *tp = &cts.cts_text_props[read_idx]; + if (tp->tp_col != MAXCOL) + { + if (read_idx != write_idx) + cts.cts_text_props[write_idx] = *tp; + write_idx++; + } + } + cts.cts_text_prop_count = write_idx; + if (cts.cts_text_prop_count == 0) + VIM_CLEAR(cts.cts_text_props); + } + + win_linetabsize_cts(&cts, (colnr_T)MAXCOL); + clear_chartabsize_arg(&cts); + return (int)cts.cts_vcol; +#endif +} + void win_linetabsize_cts(chartabsize_T *cts, colnr_T len) { @@ -1318,7 +1357,7 @@ win_lbr_chartabsize( cts->cts_bri_size = get_breakindent_win(wp, line); head_mid += cts->cts_bri_size; } - if (head_mid > 0 && wcol + size > wp->w_width) + if (head_mid > 0) { // Calculate effective window width. int prev_rem = wp->w_width - wcol; diff --git a/src/clientserver.c b/src/clientserver.c index 340add3..b19cd44 100644 --- a/src/clientserver.c +++ b/src/clientserver.c @@ -608,7 +608,7 @@ build_drop_cmd( // Call inputsave() so that a prompt for an encryption key works. ga_concat(&ga, (char_u *) - ":if exists('*inputsave')|call inputsave()|endif|"); + ":if exists('*inputsave')|call inputsave()|endif|"); if (tabs) ga_concat(&ga, (char_u *)"tab "); ga_concat(&ga, (char_u *)"drop"); @@ -652,7 +652,13 @@ build_drop_cmd( // endif // endif ga_concat(&ga, (char_u *)":if !exists('+acd')||!&acd|if haslocaldir()|"); +#ifdef MSWIN + // in case :set shellslash is set, need to normalize the directory separators + // '/' is not valid in a filename so replacing '/' by '\\' should be safe + ga_concat(&ga, (char_u *)"cd -|lcd -|elseif getcwd()->tr('/','\\') ==# '"); +#else ga_concat(&ga, (char_u *)"cd -|lcd -|elseif getcwd() ==# '"); +#endif ga_concat(&ga, cdp); ga_concat(&ga, (char_u *)"'|cd -|endif|endif"); vim_free(cdp); diff --git a/src/configure.ac b/src/configure.ac index 6311269..f6e54b3 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -4523,13 +4523,13 @@ if test "$enable_nls" = "yes"; then AC_MSG_RESULT([no]) fi AC_SUBST(MSGFMT_DESKTOP) - AC_MSG_CHECKING([if msgfmt supports --no-convert]) + AC_MSG_CHECKING([if $MSGFMT supports --no-convert]) if "$MSGFMT" --help | grep -q -- '--no-convert' >/dev/null; then AC_MSG_RESULT([yes]) - MSGFMTCMD="OLD_PO_FILE_INPUT=yes msgfmt --no-convert -v" + MSGFMTCMD="OLD_PO_FILE_INPUT=yes $MSGFMT --no-convert -v" else AC_MSG_RESULT([no]) - MSGFMTCMD="OLD_PO_FILE_INPUT=yes msgfmt -v" + MSGFMTCMD="OLD_PO_FILE_INPUT=yes $MSGFMT -v" fi AC_SUBST(MSGFMTCMD) fi diff --git a/src/dict.c b/src/dict.c index 508d00c..c78995d 100644 --- a/src/dict.c +++ b/src/dict.c @@ -1300,12 +1300,18 @@ dict_extend_func( action = tv_get_string_chk(&argvars[2]); if (action == NULL) + { + if (is_new) + dict_unref(d1); return; + } for (i = 0; i < 3; ++i) if (STRCMP(action, av[i]) == 0) break; if (i == 3) { + if (is_new) + dict_unref(d1); semsg(_(e_invalid_argument_str), action); return; } diff --git a/src/dosinst.c b/src/dosinst.c index 35625a7..116cc8f 100644 --- a/src/dosinst.c +++ b/src/dosinst.c @@ -1663,7 +1663,7 @@ install_registry(void) uninstall_string, icon_string, version_string, - "Bram Moolenaar et al."); + "The Vim Project"); if (ERROR_SUCCESS != lRet) return FAIL; diff --git a/src/drawline.c b/src/drawline.c index a8de449..81577be 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -2072,26 +2072,34 @@ win_line( // not on the next char yet, don't start another prop --bcol; # endif - int display_text_first = FALSE; - // Add any text property that starts in this column. - // With 'nowrap' and not in the first screen line only "below" - // text prop can show. - while (text_prop_next < text_prop_count - && (text_props[text_prop_next].tp_col == MAXCOL - ? ((*ptr == NUL - && (wp->w_p_wrap - || wlv.row == startrow - || (text_props[text_prop_next].tp_flags - & TP_FLAG_ALIGN_BELOW))) - || (bcol == 0 - && (text_props[text_prop_next].tp_flags - & TP_FLAG_ALIGN_ABOVE))) - : bcol >= text_props[text_prop_next].tp_col - 1)) + while (text_prop_next < text_prop_count) { - if (text_props[text_prop_next].tp_col == MAXCOL - || bcol <= text_props[text_prop_next].tp_col - 1 - + text_props[text_prop_next].tp_len) + int active; + textprop_T *tp = &text_props[text_prop_next]; + if (tp->tp_col == MAXCOL) + { + if (bcol == 0 && (tp->tp_flags & TP_FLAG_ALIGN_ABOVE)) + active = TRUE; + else if (*ptr != NUL) + break; + else + { + // With 'nowrap' and not in the first screen line only "below" + // text prop can show. + active = wp->w_p_wrap + || wlv.row == startrow + || (tp->tp_flags & TP_FLAG_ALIGN_BELOW); + } + } + else + { + if (bcol < tp->tp_col - 1) + break; + active = bcol <= tp->tp_col - 1 + tp->tp_len; + } + + if (active) text_prop_idxs[text_props_active++] = text_prop_next; ++text_prop_next; } @@ -2109,8 +2117,7 @@ win_line( text_prop_id = 0; reset_extra_attr = FALSE; } - if (text_props_active > 0 && wlv.n_extra == 0 - && !display_text_first) + if (text_props_active > 0 && wlv.n_extra == 0) { int used_tpi = -1; int used_attr = 0; @@ -2157,8 +2164,6 @@ win_line( // skip this prop, first display the '$' after // the line or display an empty line text_prop_follows = TRUE; - if (used_tpi < 0) - display_text_first = TRUE; continue; } @@ -2172,7 +2177,6 @@ win_line( text_prop_flags = pt->pt_flags; text_prop_id = tp->tp_id; used_tpi = tpi; - display_text_first = FALSE; } } if (text_prop_id < 0 && used_tpi >= 0 @@ -2327,17 +2331,29 @@ win_line( } } else if (text_prop_next < text_prop_count - && text_props[text_prop_next].tp_col == MAXCOL && ((*ptr != NUL && ptr[mb_ptr2len(ptr)] == NUL) - || (!wp->w_p_wrap - && wlv.col == wp->w_width - 1 - && (text_props[text_prop_next].tp_flags - & TP_FLAG_ALIGN_BELOW)))) + || (!wp->w_p_wrap && wlv.col == wp->w_width - 1))) + { // When at last-but-one character and a text property // follows after it, we may need to flush the line after // displaying that character. // Or when not wrapping and at the rightmost column. - text_prop_follows = TRUE; + + int only_below_follows = !wp->w_p_wrap && wlv.col == wp->w_width - 1; + // TODO: Store "after"/"right"/"below" text properties in order + // in the buffer so only `text_props[text_prop_count - 1]` + // needs to be checked for following "below" virtual text + for (int i = text_prop_next; i < text_prop_count; ++i) + { + if (text_props[i].tp_col == MAXCOL + && (!only_below_follows + || (text_props[i].tp_flags & TP_FLAG_ALIGN_BELOW))) + { + text_prop_follows = TRUE; + break; + } + } + } } if (wlv.start_extra_for_textprop) diff --git a/src/edit.c b/src/edit.c index 69ec255..075b39b 100644 --- a/src/edit.c +++ b/src/edit.c @@ -845,9 +845,10 @@ doESCkey: did_cursorhold = FALSE; // ins_redraw() triggers TextChangedI only when no characters - // are in the typeahead buffer, so only reset curbuf->b_last_changedtick + // are in the typeahead buffer, so reset curbuf->b_last_changedtick only // if the TextChangedI was not blocked by char_avail() (e.g. using :norm!) - if (!char_avail()) + // and the TextChangedI autocommand has been triggered. + if (!char_avail() && curbuf->b_last_changedtick_i == CHANGEDTICK(curbuf)) curbuf->b_last_changedtick = CHANGEDTICK(curbuf); return (c == Ctrl_O); } @@ -3958,10 +3959,9 @@ ins_del(void) * Delete one character for ins_bs(). */ static void -ins_bs_one(colnr_T *vcolp) +ins_bs_one(void) { dec_cursor(); - getvcol(curwin, &curwin->w_cursor, vcolp, NULL, NULL); if (State & REPLACE_FLAG) { // Don't delete characters before the insert point when in @@ -4211,44 +4211,69 @@ ins_bs( && (!*inserted_space_p || arrow_used)))))) { - int ts; - colnr_T vcol; + colnr_T vcol = 0; colnr_T want_vcol; - colnr_T start_vcol; + char_u *line; + char_u *ptr; + char_u *cursor_ptr; + char_u *space_ptr; + colnr_T space_vcol = 0; + int prev_space = FALSE; + colnr_T want_col; *inserted_space_p = FALSE; - // Compute the virtual column where we want to be. Since - // 'showbreak' may get in the way, need to get the last column of - // the previous character. - getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); - start_vcol = vcol; - dec_cursor(); - getvcol(curwin, &curwin->w_cursor, NULL, NULL, &want_vcol); - inc_cursor(); -#ifdef FEAT_VARTABS - if (p_sta && in_indent) + + space_ptr = ptr = line = ml_get_curline(); + cursor_ptr = line + curwin->w_cursor.col; + + // Compute virtual column of cursor position, and find the last + // whitespace before cursor that is preceded by non-whitespace. + // Use chartabsize() so that virtual text and wrapping are ignored. + while (ptr < cursor_ptr) { - ts = (int)get_sw_value(curbuf); - want_vcol = (want_vcol / ts) * ts; + int cur_space = VIM_ISWHITE(*ptr); + + if (!prev_space && cur_space) + { + space_ptr = ptr; + space_vcol = vcol; + } + vcol += chartabsize(ptr, vcol); + MB_PTR_ADV(ptr); + prev_space = cur_space; } + + // Compute the virtual column where we want to be. + want_vcol = vcol > 0 ? vcol - 1 : 0; + if (p_sta && in_indent) + want_vcol -= want_vcol % (int)get_sw_value(curbuf); else +#ifdef FEAT_VARTABS want_vcol = tabstop_start(want_vcol, get_sts_value(), curbuf->b_p_vsts_array); #else - if (p_sta && in_indent) - ts = (int)get_sw_value(curbuf); - else - ts = (int)get_sts_value(); - want_vcol = (want_vcol / ts) * ts; + want_vcol -= want_vcol % (int)get_sts_value(); #endif - // delete characters until we are at or before want_vcol - while (vcol > want_vcol && curwin->w_cursor.col > 0 - && (cc = *(ml_get_cursor() - 1), VIM_ISWHITE(cc))) - ins_bs_one(&vcol); + // Find the position to stop backspacing. + // Use chartabsize() so that virtual text and wrapping are ignored. + while (TRUE) + { + int size = chartabsize(space_ptr, space_vcol); - // insert extra spaces until we are at want_vcol - while (vcol < want_vcol) + if (space_vcol + size > want_vcol) + break; + space_vcol += size; + MB_PTR_ADV(space_ptr); + } + want_col = space_ptr - line; + + // Delete characters until we are at or before want_col. + while (curwin->w_cursor.col > want_col) + ins_bs_one(); + + // Insert extra spaces until we are at want_vcol. + for (; space_vcol < want_vcol; space_vcol++) { // Remember the first char we inserted if (curwin->w_cursor.lnum == Insstart_orig.lnum @@ -4263,13 +4288,7 @@ ins_bs( if ((State & REPLACE_FLAG)) replace_push(NUL); } - getvcol(curwin, &curwin->w_cursor, &vcol, NULL, NULL); } - - // If we are now back where we started delete one character. Can - // happen when using 'sts' and 'linebreak'. - if (vcol >= start_vcol) - ins_bs_one(&vcol); } /* @@ -4783,7 +4802,7 @@ ins_pageup(void) } tpos = curwin->w_cursor; - if (onepage(BACKWARD, 1L) == OK) + if (pagescroll(BACKWARD, 1L, FALSE) == OK) { start_arrow(&tpos); can_cindent = TRUE; @@ -4840,7 +4859,7 @@ ins_pagedown(void) } tpos = curwin->w_cursor; - if (onepage(FORWARD, 1L) == OK) + if (pagescroll(FORWARD, 1L, FALSE) == OK) { start_arrow(&tpos); can_cindent = TRUE; diff --git a/src/errors.h b/src/errors.h index 5dccc63..4387a29 100644 --- a/src/errors.h +++ b/src/errors.h @@ -2268,7 +2268,7 @@ EXTERN char e_nfa_regexp_while_converting_from_postfix_to_nfa_too_many_stats_lef EXTERN char e_nfa_regexp_not_enough_space_to_store_whole_nfa[] INIT(= N_("E876: (NFA regexp) Not enough space to store the whole NFA")); EXTERN char e_nfa_regexp_invalid_character_class_nr[] - INIT(= "E877: (NFA regexp) Invalid character class: %d"); + INIT(= N_("E877: (NFA regexp) Invalid character class: %d")); EXTERN char e_nfa_regexp_could_not_allocate_memory_for_branch_traversal[] INIT(= N_("E878: (NFA regexp) Could not allocate memory for branch traversal!")); #ifdef FEAT_SYN_HL @@ -3412,9 +3412,9 @@ EXTERN char e_invalid_class_variable_declaration_str[] EXTERN char e_invalid_type_for_object_variable_str[] INIT(= N_("E1330: Invalid type for object variable: %s")); EXTERN char e_public_must_be_followed_by_var_static_final_or_const[] - INIT(= N_("E1331: Public must be followed by \"var\" or \"static\" or \"final\" or \"const\"")); + INIT(= N_("E1331: public must be followed by \"var\" or \"static\" or \"final\" or \"const\"")); EXTERN char e_public_variable_name_cannot_start_with_underscore_str[] - INIT(= N_("E1332: Public variable name cannot start with underscore: %s")); + INIT(= N_("E1332: public variable name cannot start with underscore: %s")); EXTERN char e_cannot_access_protected_variable_str[] INIT(= N_("E1333: Cannot access protected variable \"%s\" in class \"%s\"")); // E1334 unused @@ -3532,9 +3532,9 @@ EXTERN char e_class_method_str_accessible_only_using_class_str[] EXTERN char e_object_method_str_accessible_only_using_object_str[] INIT(= N_("E1386: Object method \"%s\" accessible only using class \"%s\" object")); EXTERN char e_public_variable_not_supported_in_interface[] - INIT(= N_("E1387: Public variable not supported in an interface")); + INIT(= N_("E1387: public variable not supported in an interface")); EXTERN char e_public_keyword_not_supported_for_method[] - INIT(= N_("E1388: Public keyword not supported for a method")); + INIT(= N_("E1388: public keyword not supported for a method")); EXTERN char e_missing_name_after_implements[] INIT(= N_("E1389: Missing name after implements")); EXTERN char e_cannot_use_an_object_variable_except_with_the_new_method_str[] @@ -3585,8 +3585,40 @@ EXTERN char e_builtin_object_method_str_not_supported[] INIT(= N_("E1412: Builtin object method \"%s\" not supported")); EXTERN char e_builtin_class_method_not_supported[] INIT(= N_("E1413: Builtin class method not supported")); -#endif -// E1415 - E1499 unused (reserved for Vim9 class support) +EXTERN char e_enum_can_only_be_defined_in_vim9_script[] + INIT(= N_("E1414: Enum can only be defined in Vim9 script")); +EXTERN char e_enum_name_must_start_with_uppercase_letter_str[] + INIT(= N_("E1415: Enum name must start with an uppercase letter: %s")); +EXTERN char e_enum_cannot_extend_class[] + INIT(= N_("E1416: Enum cannot extend a class or enum")); +EXTERN char e_abstract_cannot_be_used_in_enum[] + INIT(= N_("E1417: Abstract cannot be used in an Enum")); +EXTERN char e_invalid_enum_value_declaration_str[] + INIT(= N_("E1418: Invalid enum value declaration: %s")); +EXTERN char e_not_valid_command_in_enum_str[] + INIT(= N_("E1419: Not a valid command in an Enum: %s")); +EXTERN char e_missing_endenum[] + INIT(= N_("E1420: Missing :endenum")); +EXTERN char e_using_enum_as_value_str[] + INIT(= N_("E1421: Enum \"%s\" cannot be used as a value")); +EXTERN char e_enum_value_str_not_found_in_enum_str[] + INIT(= N_("E1422: Enum value \"%s\" not found in enum \"%s\"")); +EXTERN char e_enumvalue_str_cannot_be_modified[] + INIT(= N_("E1423: Enum value \"%s.%s\" cannot be modified")); +EXTERN char e_using_enum_str_as_number[] + INIT(= N_("E1424: Using an Enum \"%s\" as a Number")); +EXTERN char e_using_enum_str_as_string[] + INIT(= N_("E1425: Using an Enum \"%s\" as a String")); +EXTERN char e_enum_str_ordinal_cannot_be_modified[] + INIT(= N_("E1426: Enum \"%s\" ordinal value cannot be modified")); +EXTERN char e_enum_str_name_cannot_be_modified[] + INIT(= N_("E1427: Enum \"%s\" name cannot be modified")); +EXTERN char e_duplicate_enum_str[] + INIT(= N_("E1428: Duplicate enum value: %s")); +EXTERN char e_class_can_only_be_used_in_script[] + INIT(= N_("E1429: Class can only be used in a script")); +#endif +// E1429 - E1499 unused (reserved for Vim9 class support) EXTERN char e_cannot_mix_positional_and_non_positional_str[] INIT(= N_("E1500: Cannot mix positional and non-positional arguments: %s")); EXTERN char e_fmt_arg_nr_unused_str[] diff --git a/src/eval.c b/src/eval.c index 69b8374..51ee604 100644 --- a/src/eval.c +++ b/src/eval.c @@ -549,6 +549,7 @@ skip_expr_concatenate( ((char_u **)gap->ga_data)[gap->ga_len - 1]; ((char_u **)gap->ga_data)[gap->ga_len - 1] = NULL; ga_clear_strings(gap); + ga_clear(freegap); } else { @@ -574,16 +575,16 @@ skip_expr_concatenate( /* * Convert "tv" to a string. - * When "convert" is TRUE convert a List into a sequence of lines. + * When "join_list" is TRUE convert a List into a sequence of lines. * Returns an allocated string (NULL when out of memory). */ char_u * -typval2string(typval_T *tv, int convert) +typval2string(typval_T *tv, int join_list) { garray_T ga; char_u *retval; - if (convert && tv->v_type == VAR_LIST) + if (join_list && tv->v_type == VAR_LIST) { ga_init2(&ga, sizeof(char), 80); if (tv->vval.v_list != NULL) @@ -595,6 +596,16 @@ typval2string(typval_T *tv, int convert) ga_append(&ga, NUL); retval = (char_u *)ga.ga_data; } + else if (tv->v_type == VAR_LIST || tv->v_type == VAR_DICT) + { + char_u *tofree; + char_u numbuf[NUMBUFLEN]; + + retval = tv2string(tv, &tofree, numbuf, 0); + // Make a copy if we have a value but it's not in allocated memory. + if (retval != NULL && tofree == NULL) + retval = vim_strsave(retval); + } else retval = vim_strsave(tv_get_string(tv)); return retval; @@ -603,13 +614,13 @@ typval2string(typval_T *tv, int convert) /* * Top level evaluation function, returning a string. Does not handle line * breaks. - * When "convert" is TRUE convert a List into a sequence of lines. + * When "join_list" is TRUE convert a List into a sequence of lines. * Return pointer to allocated memory, or NULL for failure. */ char_u * eval_to_string_eap( char_u *arg, - int convert, + int join_list, exarg_T *eap, int use_simple_function) { @@ -627,7 +638,7 @@ eval_to_string_eap( retval = NULL; else { - retval = typval2string(&tv, convert); + retval = typval2string(&tv, join_list); clear_tv(&tv); } clear_evalarg(&evalarg, NULL); @@ -638,10 +649,10 @@ eval_to_string_eap( char_u * eval_to_string( char_u *arg, - int convert, + int join_list, int use_simple_function) { - return eval_to_string_eap(arg, convert, NULL, use_simple_function); + return eval_to_string_eap(arg, join_list, NULL, use_simple_function); } /* @@ -1119,7 +1130,18 @@ get_lval_check_access( if (*p == '[' || *p == '.') break; if ((flags & GLV_READ_ONLY) == 0) - msg = e_variable_is_not_writable_str; + { + 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; + } + else + msg = e_variable_is_not_writable_str; + } break; case VIM_ACCESS_ALL: break; @@ -1134,6 +1156,91 @@ get_lval_check_access( return OK; } +/* + * Get lval information for a variable imported from script "imp_sid". On + * success, updates "lp" with the variable name, type, script ID and typval. + * The variable name starts at or after "p". + * If "rettv" is not NULL it points to the value to be assigned. This used to + * match the rhs and lhs types. + * Returns a pointer to the character after the variable name if the imported + * variable is valid and writable. + * Returns NULL if the variable is not exported or typval is not found or the + * rhs type doesn't match the lhs type or the variable is not writable. + */ + 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) +{ + ufunc_T *ufunc; + type_T *type = NULL; + int cc; + int rc = FAIL; + + p = skipwhite(p); + + import_check_sourced_sid(&imp_sid); + lp->ll_sid = imp_sid; + lp->ll_name = p; + p = find_name_end(lp->ll_name, NULL, NULL, fne_flags); + lp->ll_name_end = p; + + // check the item is exported + cc = *p; + *p = NUL; + if (find_exported(imp_sid, lp->ll_name, &ufunc, &type, NULL, NULL, + 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) + goto failed; + + dictitem_T *di = find_var_in_ht(ht, 0, lp->ll_name, TRUE); + if (di == NULL) + // script is autoloaded. So variable will be found later + goto success; + + *dip = di; + + // Check whether the variable is writable. + svar_T *sv = find_typval_in_script(&di->di_tv, imp_sid, FALSE); + if (sv != NULL && sv->sv_const != 0) + { + semsg(_(e_cannot_change_readonly_variable_str), lp->ll_name); + goto failed; + } + + // check whether variable is locked + if (value_check_lock(di->di_tv.v_lock, lp->ll_name, FALSE)) + goto failed; + + lp->ll_tv = &di->di_tv; + +success: + rc = OK; + +failed: + *p = cc; + 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]", @@ -1166,7 +1273,7 @@ get_lval( char_u *p; char_u *expr_start, *expr_end; int cc; - dictitem_T *v; + dictitem_T *v = NULL; typval_T var1; typval_T var2; int empty1 = FALSE; @@ -1300,28 +1407,13 @@ get_lval( if (*p == '.') { imported_T *import = find_imported(lp->ll_name, p - lp->ll_name, TRUE); - if (import != NULL) { - ufunc_T *ufunc; - type_T *type; - - import_check_sourced_sid(&import->imp_sid); - lp->ll_sid = import->imp_sid; - lp->ll_name = skipwhite(p + 1); - p = find_name_end(lp->ll_name, NULL, NULL, fne_flags); - lp->ll_name_end = p; - - // check the item is exported - cc = *p; - *p = NUL; - if (find_exported(import->imp_sid, lp->ll_name, &ufunc, &type, - NULL, NULL, TRUE) == -1) - { - *p = cc; + p++; // skip '.' + p = get_lval_imported(lp, rettv, import->imp_sid, p, &v, + fne_flags, vim9script); + if (p == NULL) return NULL; - } - *p = cc; } } @@ -1341,7 +1433,7 @@ get_lval( lp->ll_tv = lval_root->lr_tv; v = NULL; } - else + else if (lp->ll_tv == NULL) { cc = *p; *p = NUL; @@ -6310,9 +6402,15 @@ echo_string_core( case VAR_CLASS: { class_T *cl = tv->vval.v_class; - size_t len = 6 + (cl == NULL ? 9 : STRLEN(cl->class_name)) + 1; + 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, "class %s", + vim_snprintf((char *)r, len, "%s %s", s, cl == NULL ? "[unknown]" : (char *)cl->class_name); } break; @@ -6806,7 +6904,7 @@ find_name_end( int br_nest = 0; char_u *p; int len; - int allow_curly = (flags & FNE_ALLOW_CURLY) || !in_vim9script(); + int allow_curly = !in_vim9script(); if (expr_start != NULL) { diff --git a/src/evalfunc.c b/src/evalfunc.c index 14650ca..2064982 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -3920,7 +3920,7 @@ f_deepcopy(typval_T *argvars, typval_T *rettv) static void f_did_filetype(typval_T *argvars UNUSED, typval_T *rettv UNUSED) { - rettv->vval.v_number = did_filetype; + rettv->vval.v_number = curbuf->b_did_filetype; } /* @@ -6127,6 +6127,13 @@ f_has(typval_T *argvars, typval_T *rettv) 1 #else 0 +#endif + }, + {"dialog_con_gui", +#if defined(FEAT_CON_DIALOG) && defined(FEAT_GUI_DIALOG) + 1 +#else + 0 #endif }, {"dialog_gui", @@ -11486,15 +11493,31 @@ f_type(typval_T *argvars, typval_T *rettv) case VAR_CHANNEL: n = VAR_TYPE_CHANNEL; break; case VAR_BLOB: n = VAR_TYPE_BLOB; break; case VAR_INSTR: n = VAR_TYPE_INSTR; break; - case VAR_CLASS: n = VAR_TYPE_CLASS; break; - case VAR_OBJECT: n = VAR_TYPE_OBJECT; break; case VAR_TYPEALIAS: n = VAR_TYPE_TYPEALIAS; break; + case VAR_CLASS: + { + class_T *cl = argvars[0].vval.v_class; + if (IS_ENUM(cl)) + n = VAR_TYPE_ENUM; + else + n = VAR_TYPE_CLASS; + break; + } + case VAR_OBJECT: + { + 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: case VAR_ANY: case VAR_VOID: - internal_error_no_abort("f_type(UNKNOWN)"); - n = -1; - break; + internal_error_no_abort("f_type(UNKNOWN)"); + n = -1; + break; } rettv->vval.v_number = n; } diff --git a/src/evalvars.c b/src/evalvars.c index de9d5b2..6facbeb 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -159,6 +159,8 @@ static struct vimvar {VV_NAME("maxcol", VAR_NUMBER), NULL, VV_RO}, {VV_NAME("python3_version", VAR_NUMBER), NULL, VV_RO}, {VV_NAME("t_typealias", VAR_NUMBER), NULL, VV_RO}, + {VV_NAME("t_enum", VAR_NUMBER), NULL, VV_RO}, + {VV_NAME("t_enumvalue", VAR_NUMBER), NULL, VV_RO}, }; // shorthand @@ -262,6 +264,8 @@ evalvars_init(void) set_vim_var_nr(VV_TYPE_CLASS, VAR_TYPE_CLASS); set_vim_var_nr(VV_TYPE_OBJECT, VAR_TYPE_OBJECT); set_vim_var_nr(VV_TYPE_TYPEALIAS, VAR_TYPE_TYPEALIAS); + set_vim_var_nr(VV_TYPE_ENUM, VAR_TYPE_ENUM); + set_vim_var_nr(VV_TYPE_ENUMVALUE, VAR_TYPE_ENUMVALUE); set_vim_var_nr(VV_ECHOSPACE, sc_col - 1); @@ -658,7 +662,7 @@ eval_one_expr_in_str(char_u *p, garray_T *gap, int evaluate) if (evaluate) { *block_end = NUL; - expr_val = eval_to_string(block_start, TRUE, FALSE); + expr_val = eval_to_string(block_start, FALSE, FALSE); *block_end = '}'; if (expr_val == NULL) return NULL; @@ -775,8 +779,17 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) int eval_failed = FALSE; cctx_T *cctx = vim9compile ? eap->cookie : NULL; int count = 0; + int heredoc_in_string = FALSE; + char_u *line_arg = NULL; + char_u *nl_ptr = vim_strchr(cmd, '\n'); - if (eap->ea_getline == NULL) + if (nl_ptr != NULL) + { + heredoc_in_string = TRUE; + line_arg = nl_ptr + 1; + *nl_ptr = NUL; + } + else if (eap->ea_getline == NULL) { emsg(_(e_cannot_use_heredoc_here)); return NULL; @@ -855,12 +868,38 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) int mi = 0; int ti = 0; - vim_free(theline); - theline = eap->ea_getline(NUL, eap->cookie, 0, FALSE); - if (theline == NULL) + if (heredoc_in_string) { - semsg(_(e_missing_end_marker_str), marker); - break; + char_u *next_line; + + // heredoc in a string separated by newlines. Get the next line + // from the string. + + if (*line_arg == NUL) + { + semsg(_(e_missing_end_marker_str), marker); + break; + } + + theline = line_arg; + next_line = vim_strchr(theline, '\n'); + if (next_line == NULL) + line_arg += STRLEN(line_arg); + else + { + *next_line = NUL; + line_arg = next_line + 1; + } + } + else + { + vim_free(theline); + theline = eap->ea_getline(NUL, eap->cookie, 0, FALSE); + if (theline == NULL) + { + semsg(_(e_missing_end_marker_str), marker); + break; + } } // with "trim": skip the indent matching the :let line to find the @@ -907,6 +946,8 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) } else { + int free_str = FALSE; + if (evalstr && !eap->skip) { str = eval_all_expr_in_str(str); @@ -916,15 +957,20 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) eval_failed = TRUE; continue; } - vim_free(theline); - theline = str; + free_str = TRUE; } if (list_append_string(l, str, -1) == FAIL) break; + if (free_str) + vim_free(str); } } - vim_free(theline); + if (heredoc_in_string) + // Next command follows the heredoc in the string. + eap->nextcmd = line_arg; + else + vim_free(theline); vim_free(text_indent); if (vim9compile && cctx->ctx_skip != SKIP_YES && !eval_failed) @@ -3294,12 +3340,31 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload) } } + // and finally try + return find_var_autoload_prefix(name, 0, htp, NULL); +} + +/* + * Find variable "name" with sn_autoload_prefix. + * Return a pointer to it if found, NULL if not found. + * When "sid" > 0, use it otherwise use "current_sctx.sc_sid". + * When "htp" is not NULL set "htp" to the hashtab_T used. + * When "namep" is not NULL set "namep" to the generated name, and + * then the caller gets ownership and is responsible for freeing the name. + */ + dictitem_T * +find_var_autoload_prefix(char_u *name, int sid, hashtab_T **htp, + char_u **namep) +{ + hashtab_T *ht; + dictitem_T *ret = NULL; // When using "vim9script autoload" script-local items are prefixed but can // be used with s:name. - if (SCRIPT_ID_VALID(current_sctx.sc_sid) + int check_sid = sid > 0 ? sid : current_sctx.sc_sid; + if (SCRIPT_ID_VALID(check_sid) && (in_vim9script() || (name[0] == 's' && name[1] == ':'))) { - scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + scriptitem_T *si = SCRIPT_ITEM(check_sid); if (si->sn_autoload_prefix != NULL) { @@ -3309,20 +3374,26 @@ find_var(char_u *name, hashtab_T **htp, int no_autoload) if (auto_name != NULL) { + int free_auto_name = TRUE; ht = &globvarht; ret = find_var_in_ht(ht, 'g', auto_name, TRUE); - vim_free(auto_name); if (ret != NULL) { if (htp != NULL) *htp = ht; - return ret; + if (namep != NULL) + { + free_auto_name = FALSE; + *namep = auto_name; + } } + if (free_auto_name) + vim_free(auto_name); } } } - return NULL; + return ret; } /* @@ -3462,7 +3533,11 @@ lookup_scriptitem( hi = hash_find(ht, p); res = HASHITEM_EMPTY(hi) ? FAIL : OK; - // if not script-local, then perhaps imported + // if not script-local, then perhaps autoload-exported + if (res == FAIL && find_var_autoload_prefix(p, 0, NULL, NULL) != NULL) + res = OK; + + // if not script-local or autoload, then perhaps imported if (res == FAIL && find_imported(p, 0, FALSE) != NULL) res = OK; if (p != buffer) @@ -3858,23 +3933,40 @@ set_var_const( if (sid != 0) { + varname = NULL; if (SCRIPT_ID_VALID(sid)) - ht = &SCRIPT_VARS(sid); - varname = name; + { + char_u *auto_name = NULL; + if (find_var_autoload_prefix(name, sid, &ht, &auto_name) != NULL) + { + var_in_autoload = TRUE; + varname = auto_name; + name_tofree = varname; + } + else + ht = &SCRIPT_VARS(sid); + } + if (varname == NULL) + varname = name; } else { - scriptitem_T *si; + scriptitem_T *si; + char_u *auto_name = NULL; - if (in_vim9script() && is_export - && SCRIPT_ID_VALID(current_sctx.sc_sid) - && (si = SCRIPT_ITEM(current_sctx.sc_sid)) - ->sn_autoload_prefix != NULL) + if (in_vim9script() + && SCRIPT_ID_VALID(current_sctx.sc_sid) + && (si = SCRIPT_ITEM(current_sctx.sc_sid)) + ->sn_autoload_prefix != NULL + && (is_export + || find_var_autoload_prefix(name, 0, NULL, &auto_name) + != NULL)) { // In a vim9 autoload script an exported variable is put in the // global namespace with the autoload prefix. var_in_autoload = TRUE; - varname = concat_str(si->sn_autoload_prefix, name); + varname = auto_name != NULL ? auto_name + : concat_str(si->sn_autoload_prefix, name); if (varname == NULL) goto failed; name_tofree = varname; @@ -4143,6 +4235,7 @@ failed: * - Whether the variable is read-only * - Whether the variable value is locked * - Whether the variable is locked + * NOTE: "name" is only used for error messages. */ int var_check_permission(dictitem_T *di, char_u *name) diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 864f89d..2a5d842 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -2961,7 +2961,7 @@ do_ecmd( // Since we are starting to edit a file, consider the filetype to be // unset. Helps for when an autocommand changes files and expects syntax // highlighting to work in the other file. - did_filetype = FALSE; + curbuf->b_did_filetype = FALSE; /* * other_file oldbuf diff --git a/src/ex_cmds.h b/src/ex_cmds.h index bd26e81..70e5770 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -99,13 +99,14 @@ typedef struct exarg exarg_T; */ #ifdef DO_DECLARE_EXCMD # define EXCMD(a, b, c, d, e) \ - {(char_u *)b, c, (long_u)(d), e} + {(char_u *)b, STRLEN_LITERAL(b), c, (long_u)(d), e} typedef void (*ex_func_T) (exarg_T *eap); static struct cmdname { char_u *cmd_name; // name of the command + size_t cmd_namelen; // length of the command name ex_func_T cmd_func; // function for this command long_u cmd_argt; // flags declared above cmd_addr_T cmd_addr_type; // flag for address type @@ -595,7 +596,7 @@ EXCMD(CMD_endwhile, "endwhile", ex_endwhile, EXCMD(CMD_enew, "enew", ex_edit, EX_BANG|EX_TRLBAR, ADDR_NONE), -EXCMD(CMD_enum, "enum", ex_enum, +EXCMD(CMD_enum, "enum", ex_class, EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_EXPORT, ADDR_NONE), EXCMD(CMD_eval, "eval", ex_eval, diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index 4a6f519..ce30b8d 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -163,7 +163,7 @@ dialog_changed( char_u buff[DIALOG_MSG_SIZE]; int ret; buf_T *buf2; - exarg_T ea; + exarg_T ea; dialog_msg(buff, _("Save changes to \"%s\"?"), buf->b_fname); if (checkall) @@ -177,14 +177,31 @@ dialog_changed( if (ret == VIM_YES) { + int empty_bufname; + #ifdef FEAT_BROWSE // May get file name, when there is none browse_save_fname(buf); #endif - if (buf->b_fname != NULL && check_overwrite(&ea, buf, - buf->b_fname, buf->b_ffname, FALSE) == OK) + empty_bufname = buf->b_fname == NULL ? TRUE : FALSE; + if (empty_bufname) + buf_set_name(buf->b_fnum, (char_u *)"Untitled"); + + if (check_overwrite(&ea, buf, buf->b_fname, buf->b_ffname, FALSE) == OK) + { // didn't hit Cancel - (void)buf_write_all(buf, FALSE); + if (buf_write_all(buf, FALSE) == OK) + return; + } + + // restore to empty when write failed + if (empty_bufname) + { + VIM_CLEAR(buf->b_fname); + VIM_CLEAR(buf->b_ffname); + VIM_CLEAR(buf->b_sfname); + unchanged(buf, TRUE, FALSE); + } } else if (ret == VIM_NO) { diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 19b1d85..a588f26 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -19,6 +19,10 @@ static int ex_pressedreturn = FALSE; # define ex_hardcopy ex_ni #endif +#if defined(FEAT_EVAL) || defined(PROTO) +static int cmp_cmdmod_info(const void *a, const void *b); +#endif + #ifdef FEAT_EVAL static char_u *do_one_cmd(char_u **, int, cstack_T *, char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); #else @@ -82,7 +86,7 @@ static void correct_range(exarg_T *eap); #ifdef FEAT_QUICKFIX static char_u *replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep); #endif -static char_u *repl_cmdline(exarg_T *eap, char_u *src, int srclen, char_u *repl, char_u **cmdlinep); +static char_u *repl_cmdline(exarg_T *eap, char_u *src, size_t srclen, char_u *repl, char_u **cmdlinep); static void ex_highlight(exarg_T *eap); static void ex_colorscheme(exarg_T *eap); static void ex_cquit(exarg_T *eap); @@ -460,6 +464,40 @@ restore_dbg_stuff(struct dbg_stuff *dsp) } #endif +/* + * Check if ffname differs from fnum. + * fnum is a buffer number. 0 == current buffer, 1-or-more must be a valid buffer ID. + * ffname is a full path to where a buffer lives on-disk or would live on-disk. + * + */ + static int +is_other_file(int fnum, char_u *ffname) +{ + if (fnum != 0) + { + if (fnum == curbuf->b_fnum) + return FALSE; + + return TRUE; + } + + if (ffname == NULL) + return TRUE; + + if (*ffname == NUL) + return FALSE; + + // TODO: Need a reliable way to know whether a buffer is meant to live on-disk + // !curbuf->b_dev_valid is not always available (example: missing on Windows) + if (curbuf->b_sfname != NULL + && *curbuf->b_sfname != NUL) + // This occurs with unsaved buffers. In which case `ffname` + // actually corresponds to curbuf->b_sfname + return fnamecmp(ffname, curbuf->b_sfname) != 0; + + return otherfile(ffname); +} + /* * do_exmode(): Repeatedly get commands for the "Ex" mode, until the ":vi" * command is given. @@ -2911,7 +2949,7 @@ parse_command_modifiers( switch (*p) { - // When adding an entry, also modify cmdmods[]. + // When adding an entry, also modify cmdmod_info_tab[]. case 'a': if (!checkforcmd_noparen(&eap->cmd, "aboveleft", 3)) break; cmod->cmod_split |= WSP_ABOVE; @@ -3921,7 +3959,7 @@ find_ex_command( if (eap->cmdidx == CMD_mode || eap->cmdidx == CMD_Print) eap->cmdidx = CMD_SIZE; else if ((cmdnames[eap->cmdidx].cmd_argt & EX_WHOLE) - && len < (int)STRLEN(cmdnames[eap->cmdidx].cmd_name)) + && len < (int)cmdnames[eap->cmdidx].cmd_namelen) { semsg(_(e_command_cannot_be_shortened_str), eap->cmd); eap->cmdidx = CMD_SIZE; @@ -3970,12 +4008,14 @@ find_ex_command( } #if defined(FEAT_EVAL) || defined(PROTO) -static struct cmdmod +typedef struct { char *name; int minlen; int has_count; // :123verbose :3tab -} cmdmods[] = { +} cmdmod_info_T; + +static cmdmod_info_T cmdmod_info_tab[] = { {"aboveleft", 3, FALSE}, {"belowright", 3, FALSE}, {"botright", 2, FALSE}, @@ -4001,8 +4041,18 @@ static struct cmdmod {"unsilent", 3, FALSE}, {"verbose", 4, TRUE}, {"vertical", 4, FALSE}, - {"vim9cmd", 4, FALSE}, -}; + {"vim9cmd", 4, FALSE} +}; // cmdmod_info_tab + +// compare two cmdmod_info_T structs by case sensitive name with length + static int +cmp_cmdmod_info(const void *a, const void *b) +{ + cmdmod_info_T *cm1 = (cmdmod_info_T *)a; + cmdmod_info_T *cm2 = (cmdmod_info_T *)b; + + return STRNCMP(cm1->name, cm2->name, cm2->minlen); +} /* * Return length of a command modifier (including optional count). @@ -4011,20 +4061,36 @@ static struct cmdmod int modifier_len(char_u *cmd) { - int i, j; char_u *p = cmd; + cmdmod_info_T target; + cmdmod_info_T *entry; if (VIM_ISDIGIT(*cmd)) p = skipwhite(skipdigits(cmd + 1)); - for (i = 0; i < (int)ARRAY_LENGTH(cmdmods); ++i) + + // only lowercase characters can match + if (!ASCII_ISLOWER(*p)) + return 0; + + target.name = (char *)p; + target.minlen = 0; // not used, see cmp_cmdmod_info() + target.has_count = FALSE; + + entry = (cmdmod_info_T *)bsearch(&target, &cmdmod_info_tab, ARRAY_LENGTH(cmdmod_info_tab), sizeof(cmdmod_info_tab[0]), cmp_cmdmod_info); + if (entry != NULL) { - for (j = 0; p[j] != NUL; ++j) - if (p[j] != cmdmods[i].name[j]) + int i; + + for (i = entry->minlen; p[i] != NUL; ++i) + { + if (p[i] != entry->name[i]) break; - if (!ASCII_ISALPHA(p[j]) && j >= cmdmods[i].minlen - && (p == cmd || cmdmods[i].has_count)) - return j + (int)(p - cmd); + } + + if (!ASCII_ISALPHA(p[i]) && i >= entry->minlen && (p == cmd || entry->has_count)) + return i + (int)(p - cmd); } + return 0; } @@ -4038,18 +4104,33 @@ cmd_exists(char_u *name) { exarg_T ea; int full = FALSE; - int i; - int j; char_u *p; - // Check command modifiers. - for (i = 0; i < (int)ARRAY_LENGTH(cmdmods); ++i) + // only lowercase characters can match + if (ASCII_ISLOWER(*name)) { - for (j = 0; name[j] != NUL; ++j) - if (name[j] != cmdmods[i].name[j]) - break; - if (name[j] == NUL && j >= cmdmods[i].minlen) - return (cmdmods[i].name[j] == NUL ? 2 : 1); + cmdmod_info_T target; + cmdmod_info_T *entry; + + target.name = (char *)name; + target.minlen = 0; // not used, see cmp_cmdmod_info() + target.has_count = FALSE; + + // Check command modifiers. + entry = (cmdmod_info_T *)bsearch(&target, &cmdmod_info_tab, ARRAY_LENGTH(cmdmod_info_tab), sizeof(cmdmod_info_tab[0]), cmp_cmdmod_info); + if (entry != NULL) + { + int i; + + for (i = entry->minlen; name[i] != NUL; ++i) + { + if (name[i] != entry->name[i]) + break; + } + + if (name[i] == NUL && i >= entry->minlen) + return (entry->name[i] == NUL ? 2 : 1); + } } // Check built-in commands and user defined commands. @@ -4963,12 +5044,14 @@ replace_makeprg(exarg_T *eap, char_u *p, char_u **cmdlinep) } else { - new_cmdline = alloc(STRLEN(program) + STRLEN(p) + 2); + size_t program_len = STRLEN(program); + + new_cmdline = alloc(program_len + STRLEN(p) + 2); if (new_cmdline == NULL) return NULL; // out of memory STRCPY(new_cmdline, program); - STRCAT(new_cmdline, " "); - STRCAT(new_cmdline, p); + STRCPY(new_cmdline + program_len, " "); + STRCPY(new_cmdline + program_len + 1, p); } msg_make(p); @@ -5118,7 +5201,7 @@ expand_filename( } } - p = repl_cmdline(eap, p, srclen, repl, cmdlinep); + p = repl_cmdline(eap, p, (size_t)srclen, repl, cmdlinep); vim_free(repl); if (p == NULL) return FAIL; @@ -5189,7 +5272,7 @@ expand_filename( } if (p != NULL) { - (void)repl_cmdline(eap, eap->arg, (int)STRLEN(eap->arg), + (void)repl_cmdline(eap, eap->arg, STRLEN(eap->arg), p, cmdlinep); if (n == 2) // p came from ExpandOne() vim_free(p); @@ -5212,23 +5295,26 @@ expand_filename( repl_cmdline( exarg_T *eap, char_u *src, - int srclen, + size_t srclen, char_u *repl, char_u **cmdlinep) { - int len; - int i; + size_t repllen; + size_t taillen; + size_t i; char_u *new_cmdline; + size_t new_cmdlinelen; /* * The new command line is build in new_cmdline[]. * First allocate it. * Careful: a "+cmd" argument may have been NUL terminated. */ - len = (int)STRLEN(repl); - i = (int)(src - *cmdlinep) + (int)STRLEN(src + srclen) + len + 3; + repllen = STRLEN(repl); + taillen = STRLEN(src + srclen); + i = (src - *cmdlinep) + repllen + taillen + 3; if (eap->nextcmd != NULL) - i += (int)STRLEN(eap->nextcmd);// add space for next command + i += STRLEN(eap->nextcmd); // add space for next command if ((new_cmdline = alloc(i)) == NULL) return NULL; // out of memory! @@ -5238,19 +5324,19 @@ repl_cmdline( * Copy what came after the expanded part. * Copy the next commands, if there are any. */ - i = (int)(src - *cmdlinep); // length of part before match - mch_memmove(new_cmdline, *cmdlinep, (size_t)i); + new_cmdlinelen = src - *cmdlinep; // length of part before replacement + mch_memmove(new_cmdline, *cmdlinep, new_cmdlinelen); - mch_memmove(new_cmdline + i, repl, (size_t)len); - i += len; // remember the end of the string - STRCPY(new_cmdline + i, src + srclen); - src = new_cmdline + i; // remember where to continue + mch_memmove(new_cmdline + new_cmdlinelen, repl, repllen); + new_cmdlinelen += repllen; // remember the end of the string + STRCPY(new_cmdline + new_cmdlinelen, src + srclen); + src = new_cmdline + new_cmdlinelen; // remember where to continue if (eap->nextcmd != NULL) // append next command { - i = (int)STRLEN(new_cmdline) + 1; - STRCPY(new_cmdline + i, eap->nextcmd); - eap->nextcmd = new_cmdline + i; + new_cmdlinelen += taillen + 1; + STRCPY(new_cmdline + new_cmdlinelen, eap->nextcmd); + eap->nextcmd = new_cmdline + new_cmdlinelen; } eap->cmd = new_cmdline + (eap->cmd - *cmdlinep); eap->arg = new_cmdline + (eap->arg - *cmdlinep); @@ -5421,9 +5507,10 @@ get_bad_name(expand_T *xp UNUSED, int idx) "drop", }; - if (idx < (int)ARRAY_LENGTH(p_bad_values)) - return (char_u*)p_bad_values[idx]; - return NULL; + if (idx < 0 || idx >= (int)ARRAY_LENGTH(p_bad_values)) + return NULL; + + return (char_u*)p_bad_values[idx]; } /* @@ -5537,9 +5624,10 @@ get_argopt_name(expand_T *xp UNUSED, int idx) "edit", }; - if (idx < (int)ARRAY_LENGTH(p_opt_values)) - return (char_u*)p_opt_values[idx]; - return NULL; + if (idx < 0 || idx >= (int)ARRAY_LENGTH(p_opt_values)) + return NULL; + + return (char_u*)p_opt_values[idx]; } /* @@ -6317,11 +6405,19 @@ get_tabpage_arg(exarg_T *eap) else { tab_number = eap->line2; - if (!unaccept_arg0 && *skipwhite(*eap->cmdlinep) == '-') + if (!unaccept_arg0) { - --tab_number; - if (tab_number < unaccept_arg0) - eap->errmsg = _(e_invalid_range); + char_u *cmdp = eap->cmd; + + while (--cmdp > *eap->cmdlinep + && (VIM_ISWHITE(*cmdp) || VIM_ISDIGIT(*cmdp))) + ; + if (*cmdp == '-') + { + --tab_number; + if (tab_number < unaccept_arg0) + eap->errmsg = _(e_invalid_range); + } } } } @@ -7248,12 +7344,15 @@ ex_open(exarg_T *eap) static void ex_edit(exarg_T *eap) { + char_u *ffname = eap->cmdidx == CMD_enew ? NULL : eap->arg; + // Exclude commands which keep the window's current buffer if ( eap->cmdidx != CMD_badd && eap->cmdidx != CMD_balt // All other commands must obey 'winfixbuf' / ! rules - && !check_can_set_curbuf_forceit(eap->forceit)) + && (is_other_file(0, ffname) && !check_can_set_curbuf_forceit(eap->forceit)) + ) return; do_exedit(eap, NULL); @@ -9237,6 +9336,27 @@ ex_tag_cmd(exarg_T *eap, char_u *name) eap->forceit, TRUE); } +enum { + SPEC_PERC = 0, + SPEC_HASH, + SPEC_CWORD, // cursor word + SPEC_CCWORD, // cursor WORD + SPEC_CEXPR, // expr under cursor + SPEC_CFILE, // cursor path name + SPEC_SFILE, // ":so" file name + SPEC_SLNUM, // ":so" file line number + SPEC_STACK, // call stack + SPEC_SCRIPT, // script file name + SPEC_AFILE, // autocommand file name + SPEC_ABUF, // autocommand buffer number + SPEC_AMATCH, // autocommand match name + SPEC_SFLNUM, // script file line number + SPEC_SID // script ID: 123_ +#ifdef FEAT_CLIENTSERVER + , SPEC_CLIENT +#endif +}; + /* * Check "str" for starting with a special cmdline variable. * If found return one of the SPEC_ values and set "*usedlen" to the length of @@ -9245,54 +9365,54 @@ ex_tag_cmd(exarg_T *eap, char_u *name) int find_cmdline_var(char_u *src, int *usedlen) { - int len; - int i; - static char *(spec_str[]) = { - "%", -#define SPEC_PERC 0 - "#", -#define SPEC_HASH (SPEC_PERC + 1) - "", // cursor word -#define SPEC_CWORD (SPEC_HASH + 1) - "", // cursor WORD -#define SPEC_CCWORD (SPEC_CWORD + 1) - "", // expr under cursor -#define SPEC_CEXPR (SPEC_CCWORD + 1) - "", // cursor path name -#define SPEC_CFILE (SPEC_CEXPR + 1) - "", // ":so" file name -#define SPEC_SFILE (SPEC_CFILE + 1) - "", // ":so" file line number -#define SPEC_SLNUM (SPEC_SFILE + 1) - "", // call stack -#define SPEC_STACK (SPEC_SLNUM + 1) - "