diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 1d104bc720..a1d0e14c53 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1,4 +1,4 @@ -*editing.txt* For Vim version 7.4. Last change: 2016 Jan 03 +*editing.txt* For Vim version 7.4. Last change: 2016 Jan 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -639,6 +639,7 @@ list of the current window. :0argadd x x a b c :1argadd x a x b c :$argadd x a b c x + And after the last one: :+2argadd y a b c x y There is no check for duplicates, it is possible to add a file to the argument list twice. @@ -1458,6 +1459,16 @@ using zip, "[blowfish]" when using blowfish, etc. When writing an undo file, the same key and method will be used for the text in the undo file. |persistent-undo|. +To test for blowfish support you can use these conditions: > + has('crypt-blowfish') + has('crypt-blowfish2') +This works since Vim 7.4.1099 while blowfish support was added earlier. +Thus the condition failing doesn't mean blowfish is not supported. You can +test for blowfish with: > + v:version >= 703 +And for blowfish2 with: > + v:version > 704 || (v:version == 704 && has('patch401')) +< *E817* *E818* *E819* *E820* When encryption does not work properly, you would be able to write your text to a file and never be able to read it back. Therefore a test is performed to diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 7d83238a5c..f92ca2d8ac 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Jan 16 +*eval.txt* For Vim version 7.4. Last change: 2016 Jan 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -873,7 +873,7 @@ cursor: > :let c = getline(".")[col(".") - 1] If the length of the String is less than the index, the result is an empty -String. A negative index always results in an empty string (reason: backwards +String. A negative index always results in an empty string (reason: backward compatibility). Use [-1:] to get the last byte. If expr8 is a |List| then it results the item at index expr1. See |list-index| @@ -1851,7 +1851,7 @@ getcmdpos() Number return cursor position in command-line getcmdtype() String return current command-line type getcmdwintype() String return current command-line window type getcurpos() List position of the cursor -getcwd() String the current working directory +getcwd( [{winnr} [, {tabnr}]]) String get the current working directory getfontname( [{name}]) String name of font being used getfperm( {fname}) String file permissions of file {fname} getfsize( {fname}) Number size in bytes of file {fname} @@ -1882,7 +1882,8 @@ globpath( {path}, {expr} [, {nosuf} [, {list} [, {alllinks}]]]) String do glob({expr}) for all dirs in {path} has( {feature}) Number TRUE if feature {feature} supported has_key( {dict}, {key}) Number TRUE if {dict} has entry {key} -haslocaldir() Number TRUE if current window executed |:lcd| +haslocaldir( [{winnr} [, {tabnr}]]) + Number TRUE if the window executed |:lcd| hasmapto( {what} [, {mode} [, {abbr}]]) Number TRUE if mapping to {what} exists histadd( {history},{item}) String add an item to a history @@ -1950,6 +1951,7 @@ nextnonblank( {lnum}) Number line nr of non-blank line >= {lnum} nr2char( {expr}[, {utf8}]) String single char with ASCII/UTF8 value {expr} or( {expr}, {expr}) Number bitwise OR pathshorten( {expr}) String shorten directory names in a path +perleval( {expr}) any evaluate |Perl| expression pow( {x}, {y}) Float {x} to the power of {y} prevnonblank( {lnum}) Number line nr of non-blank line <= {lnum} printf( {fmt}, {expr1}...) String format text @@ -3521,8 +3523,16 @@ getcurpos() Get the position of the cursor. This is like getpos('.'), but call setpos('.', save_cursor) < *getcwd()* -getcwd() The result is a String, which is the name of the current +getcwd([{winnr} [, {tabnr}]]) + The result is a String, which is the name of the current working directory. + Without arguments, for the current window. + + With {winnr} return the local current directory of this window + in the current tab page. + With {winnr} and {tabnr} return the local current directory of + the window in the specified tab page. + Return an empty string if the arguments are invalid. getfsize({fname}) *getfsize()* The result is a Number, which is the size in bytes of the @@ -3858,9 +3868,15 @@ has_key({dict}, {key}) *has_key()* The result is a Number, which is 1 if |Dictionary| {dict} has an entry with key {key}. Zero otherwise. -haslocaldir() *haslocaldir()* - The result is a Number, which is 1 when the current - window has set a local path via |:lcd|, and 0 otherwise. +haslocaldir([{winnr} [, {tabnr}]]) *haslocaldir()* + The result is a Number, which is 1 when the window has set a + local path via |:lcd|, and 0 otherwise. + + Without arguments use the current window. + With {winnr} use this window in the current tab page. + With {winnr} and {tabnr} use the window in the specified tab + page. + Return 0 if the arguments are invalid. hasmapto({what} [, {mode} [, {abbr}]]) *hasmapto()* The result is a Number, which is 1 if there is a mapping that @@ -4547,8 +4563,8 @@ matchadd({group}, {pattern}[, {priority}[, {id} [, {dict}]]]) respectively. If the {id} argument is not specified or -1, |matchadd()| automatically chooses a free ID. - The optional {dict} argmument allows for further custom - values. Currently this is used to specify a match specifc + The optional {dict} argument allows for further custom + values. Currently this is used to specify a match specific conceal character that will be shown for |hl-Conceal| highlighted matches. The dict can have the following members: @@ -4778,6 +4794,17 @@ pathshorten({expr}) *pathshorten()* < ~/.v/a/myfile.vim ~ It doesn't matter if the path exists or not. +perleval({expr}) *perleval()* + Evaluate Perl expression {expr} in scalar context and return + its result converted to Vim data structures. If value can't be + converted, it is returned as a string Perl representation. + Note: If you want an array or hash, {expr} must return a + reference to it. + Example: > + :echo perleval('[1 .. 4]') +< [1, 2, 3, 4] + {only available when compiled with the |+perl| feature} + pow({x}, {y}) *pow()* Return the power of {x} to the exponent {y} as a |Float|. {x} and {y} must evaluate to a |Float| or a |Number|. @@ -5292,7 +5319,7 @@ search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) *search()* 'ignorecase', 'smartcase' and 'magic' are used. - When the 'z' flag is not given seaching always starts in + When the 'z' flag is not given, searching always starts in column zero and then matches before the cursor are skipped. When the 'c' flag is present in 'cpo' the next search starts after the match. Without the 'c' flag the next search starts @@ -7658,7 +7685,7 @@ This does NOT work: > From Vim version 4.5 until 5.0, every Ex command in between the ":if" and ":endif" is ignored. These two commands were just to allow for future expansions in a - backwards compatible way. Nesting was allowed. Note + backward compatible way. Nesting was allowed. Note that any ":else" or ":elseif" was ignored, the "else" part was not executed either. diff --git a/runtime/doc/if_mzsch.txt b/runtime/doc/if_mzsch.txt index f1a0bd7aa5..388a1ee060 100644 --- a/runtime/doc/if_mzsch.txt +++ b/runtime/doc/if_mzsch.txt @@ -1,4 +1,4 @@ -*if_mzsch.txt* For Vim version 7.4. Last change: 2012 Dec 17 +*if_mzsch.txt* For Vim version 7.4. Last change: 2016 Jan 16 VIM REFERENCE MANUAL by Sergey Khorev diff --git a/runtime/doc/mlang.txt b/runtime/doc/mlang.txt index eb95660d00..808bc61d4d 100644 --- a/runtime/doc/mlang.txt +++ b/runtime/doc/mlang.txt @@ -1,4 +1,4 @@ -*mlang.txt* For Vim version 7.4. Last change: 2012 Jan 15 +*mlang.txt* For Vim version 7.4. Last change: 2016 Jan 16 VIM REFERENCE MANUAL by Bram Moolenaar @@ -97,13 +97,15 @@ If you used the self-installing .exe file, message translations should work already. Otherwise get the libintl.dll file if you don't have it yet: http://sourceforge.net/projects/gettext +Or: + https://mlocati.github.io/gettext-iconv-windows/ This also contains tools xgettext, msgformat and others. libintl.dll should be placed in same directory with (g)vim.exe, or some -place where PATH environment value describe. Message files (vim.mo) -have to be placed in "$VIMRUNTIME/lang/xx/LC_MESSAGES", where "xx" is the -abbreviation of the language (mostly two letters). +place where PATH environment value describe. Vim also finds libintl-8.dll. +Message files (vim.mo) have to be placed in "$VIMRUNTIME/lang/xx/LC_MESSAGES", +where "xx" is the abbreviation of the language (mostly two letters). If you write your own translations you need to generate the .po file and convert it to a .mo file. You need to get the source distribution and read diff --git a/runtime/doc/tags b/runtime/doc/tags index 746a76ffbf..cb6922a5c7 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -3404,12 +3404,18 @@ $VIMRUNTIME starting.txt /*$VIMRUNTIME* == change.txt /*==* > change.txt /*>* >> change.txt /*>>* +>backtrace repeat.txt /*>backtrace* +>bt repeat.txt /*>bt* >cont repeat.txt /*>cont* +>down repeat.txt /*>down* >finish repeat.txt /*>finish* +>frame repeat.txt /*>frame* >interrupt repeat.txt /*>interrupt* >next repeat.txt /*>next* >quit repeat.txt /*>quit* >step repeat.txt /*>step* +>up repeat.txt /*>up* +>where repeat.txt /*>where* ? pattern.txt /*?* ? pattern.txt /*?* @ repeat.txt /*@* @@ -7179,6 +7185,7 @@ mzscheme-examples if_mzsch.txt /*mzscheme-examples* mzscheme-funcref if_mzsch.txt /*mzscheme-funcref* mzscheme-mzeval if_mzsch.txt /*mzscheme-mzeval* mzscheme-sandbox if_mzsch.txt /*mzscheme-sandbox* +mzscheme-setup if_mzsch.txt /*mzscheme-setup* mzscheme-threads if_mzsch.txt /*mzscheme-threads* mzscheme-vim if_mzsch.txt /*mzscheme-vim* mzscheme-vimext if_mzsch.txt /*mzscheme-vimext* @@ -7625,6 +7632,7 @@ perl-overview if_perl.txt /*perl-overview* perl-patterns pattern.txt /*perl-patterns* perl-using if_perl.txt /*perl-using* perl.vim syntax.txt /*perl.vim* +perleval() eval.txt /*perleval()* persistent-undo undo.txt /*persistent-undo* pexpr-option print.txt /*pexpr-option* pfn-option print.txt /*pfn-option* diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index a2fc617cf6..bc7b962555 100644 --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1,4 +1,4 @@ -*todo.txt* For Vim version 7.4. Last change: 2016 Jan 15 +*todo.txt* For Vim version 7.4. Last change: 2016 Jan 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -97,8 +97,6 @@ Should use /usr/local/share/applications or /usr/share/applications. Or use $XDG_DATA_DIRS. Also need to run update-desktop-database (Kuriyama Kazunobu, 2015 Nov 4) -Add has('crypt-blowfish') and has('crypt-blowfish2') (Smu Johnson) - Access to uninitialized memory in match_backref() regexp_nda.c:4882 (Dominique Pelle, 2015 Nov 6) @@ -125,42 +123,16 @@ github with a URL like this: https://github.com/vim/vim/compare/v7.4.920%5E...v7.4.920.diff Diff for version.c contains more context, can't skip a patch. > -Can src/GvimExt/Make_cyg.mak be removed? -Same for src/xxd/Make_cyg.mak - When t_Co is changed from termresponse, the OptionSet autocmmand event isn't triggered. Use the code from the end of set_num_option() in set_color_count(). Python: ":py raw_input('prompt')" doesn't work. (Manu Hack) -Patch to fix cursor position in right-left mode with concealing. -(Hirohito Higashi, 2016 Jan 13) - Plugin to use Vim in MANPAGER. Konfekt, PR #491 Using uninitialized memory. (Dominique Pelle, 2015 Nov 4) -Patch for explaining the help. (Christian Brabandt, 2015 Jan 8) -Should be in the user manual? - -Patch to recognize string slice for variable followed by colon. -(Hirohito Higashi, 2015 Nov 24) - -Patch to add debug backtrace. (Alberto Fanjul, 2015 Sep 27) -Update 2016 Jan 2. Issue #433 - -Patch to gvim.nsi for appveyor build. (Ken Takata, 2016 Jan 12) - -Patch to improve behavior of dead keys on MS-Windows. (John Wellesz, 2015 Aug -25) https://github.com/vim/vim/pull/399.diff - -Patch to make mzscheme (racket) interface work. (Yukihiro Nakadaira, 2015 Jan -10) Doesn't work for me, need to build from source. Include anyway? -Additional patch by Ken Takata, 2016 Jan 13. -Merged patch by Yasuhiro Nakadaira,, 2016 Jan 14. -Update for INSSTALLpc.txt by Ken Takata, Jan 14. - MS-Windows: When editing a file with a leading space, writing it uses the wrong name. (Aram, 2014 Nov 7) Vim 7.4. @@ -171,19 +143,12 @@ Half-finished patch to fix the Problem using cgn to change a search hit when replacement includes hit. Reported by John Beckett, fix by Christian Brabandt, 2016 Jan 11. -Patch to fix pointer cast warning in VS2015. (Mike Williams, 2015 Dec 13) -Patch to make building GVimExt with VS2015. (Mike Williams, 2015 Dec 13) - Value returned by virtcol() changes depending on how lines wrap. This is inconsistent with the documentation. -Patch to add perleval(). (Damien, 2015 Dec 8, update 2016 Jan 4) - Can we cache the syntax attributes, so that updates for 'relativenumber' and 'cursorline'/'cursorcolumn' are a lot faster? -Patch to add window and tab arguments to getcwd(). (Thinca, 2015 Nov 15) - Build with Python on Mac does not always use the right library. (Kazunobu Kuriyama, 2015 Mar 28) @@ -286,7 +251,7 @@ Is this the right solution? Need to cleanup langmap behavior: - Remove LANGMAP_ADJUST() in other parts of the code. Make sure the mode is covered by the above change. So that replaying the register doesn't use keymap/langmap and still does the -same thing. Remarks on issue 543. +same thing. Remarks on issue 543 (Roland Puntaier). Patch to add grepfile(). (Scott Prager, 2015 May 26) Work in progress. diff --git a/runtime/doc/usr_02.txt b/runtime/doc/usr_02.txt index ba29dc0e9d..a3e9bf3c82 100644 --- a/runtime/doc/usr_02.txt +++ b/runtime/doc/usr_02.txt @@ -1,4 +1,4 @@ -*usr_02.txt* For Vim version 7.4. Last change: 2016 Jan 15 +*usr_02.txt* For Vim version 7.4. Last change: 2016 Jan 16 VIM USER MANUAL - by Bram Moolenaar @@ -554,38 +554,42 @@ Summary: *help-summary* > 8) Ex-commands always start with ":", so to go to the :s command help: > :help :s -9) Key combinations. They usually start with a single letter indicating - the mode for which they can be used. E.g.: > - :help i_CTRL-X -< takes you to the family of Ctrl-X commands for insert mode which can be - used to auto complete different things. Note, that certain keys will - always be written the same, e.g. Control will always be CTRL. - For normal mode commands there is no prefix and the topic is available at - :h CTRL-. E.g. > - :help CTRL-W -< In contrast > - :help c_CTRL-R -< will describe what the Ctrl-R does when entering commands in the Command - line and > - :help v_Ctrl-A -< talks about incrementing numbers in visual mode and > - :help g_CTRL-A -< talks about the g command (e.g. you have to press "g" then ). - Here the "g" stand for the normal command "g" which always expects a second - key before doing something similar to the commands starting with "z" +9) Commands specifically for debugging start with ">". To go to to the help + for the "cont" debug command: > + :help >cont -10) Regexp items always start with /. So to get help for the "\+" quantifier +10) Key combinations. They usually start with a single letter indicating + the mode for which they can be used. E.g.: > + :help i_CTRL-X +< takes you to the family of Ctrl-X commands for insert mode which can be + used to auto complete different things. Note, that certain keys will + always be written the same, e.g. Control will always be CTRL. + For normal mode commands there is no prefix and the topic is available at + :h CTRL-. E.g. > + :help CTRL-W +< In contrast > + :help c_CTRL-R +< will describe what the Ctrl-R does when entering commands in the Command + line and > + :help v_Ctrl-A +< talks about incrementing numbers in visual mode and > + :help g_CTRL-A +< talks about the g command (e.g. you have to press "g" then ). + Here the "g" stand for the normal command "g" which always expects a second + key before doing something similar to the commands starting with "z" + +11) Regexp items always start with /. So to get help for the "\+" quantifier in Vim regexes: > :help /\+ -< If you need to know everything about regular expressions, start reading - at: > +< If you need to know everything about regular expressions, start reading + at: > :help pattern.txt -11) Registers always start with "quote". To find out about the special ":" +12) Registers always start with "quote". To find out about the special ":" register: > :help quote: -12) Vim Script (VimL) is available at > +13) Vim Script (VimL) is available at > :help eval.txt < Certain aspects of the language are available at :h expr-X where "X" is a single letter. E.g. > @@ -600,7 +604,7 @@ Summary: *help-summary* > < talks about the append VimL function rather than how to append text in the current buffer. -13) Mappings are talked about in the help page :h |map.txt|. Use > +14) Mappings are talked about in the help page :h |map.txt|. Use > :help mapmode-i < to find out about the |:imap| command. Also use :map-topic to find out about certain subtopics particular for mappings. e.g: > @@ -609,19 +613,19 @@ Summary: *help-summary* > :help map-bar < for how the '|' is handled in mappings. -14) Command definitions are talked about :h command-topic, so use > +15) Command definitions are talked about :h command-topic, so use > :help command-bar < to find out about the '!' argument for custom commands. -15) Window management commands always start with CTRL-W, so you find the +16) Window management commands always start with CTRL-W, so you find the corresponding help at :h CTRL-W_letter. E.g. > :help CTRL-W_p -< for moving the previous accessed window). You can also access > +< for moving the previous accessed window. You can also access > :help windows.txt < and read your way through if you are looking for window handling commands. -16) Use |:helpgrep| to search in all help pages (and also of any installed +17) Use |:helpgrep| to search in all help pages (and also of any installed plugins). See |:helpgrep| for how to use it. To search for a topic: > :helpgrep topic @@ -632,7 +636,7 @@ Summary: *help-summary* > :copen < Move around to the match you like and press Enter to jump to that help. -17) The user manual. This describes help topics for beginners in a rather +18) The user manual. This describes help topics for beginners in a rather friendly way. Start at |usr_toc.txt| to find the table of content (as you might have guessed): > :help usr_toc.txt @@ -645,31 +649,31 @@ Summary: *help-summary* > :help 10.1 < goes to chapter 10.1 in |usr_10.txt| and talks about recording macros. -18) Highlighting groups. Always start with hl-groupname. E.g. > +19) Highlighting groups. Always start with hl-groupname. E.g. > :help hl-WarningMsg < talks about the WarningMsg highlighting group. -19) Syntax highlighting is namespaced to :syn-topic e.g. > +20) Syntax highlighting is namespaced to :syn-topic e.g. > :help :syn-conceal < talks about the conceal argument for the :syn command. -20) Quickfix commands usually start with :c while location list commands +21) Quickfix commands usually start with :c while location list commands usually start with :l -21) Autocommand events can be found by their name: > +22) Autocommand events can be found by their name: > :help BufWinLeave < To see all possible events: > :help autocommands-events -22) Command-line switches always start with "-". So for the help of the -f +23) Command-line switches always start with "-". So for the help of the -f command switch of Vim use: > :help -f -23) Optional features always start with "+". To find out about the +24) Optional features always start with "+". To find out about the conceal feature use: > :help +conceal -24) Documentation for included filetype specific functionality is usually +25) Documentation for included filetype specific functionality is usually available in the form ft--. So > :help ft-c-syntax < talks about the C syntax file and the option it provides. Sometimes, @@ -679,7 +683,7 @@ Summary: *help-summary* > :help ft-tex-plugin < are available. -25) Error and Warning codes can be looked up directly in the help. So > +26) Error and Warning codes can be looked up directly in the help. So > :help E297 < takes you exactly to the description of the swap error message and > :help W10 diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 2194dc14bc..7ef10e3b42 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -921,6 +921,7 @@ Various: *various-functions* luaeval() evaluate Lua expression mzeval() evaluate |MzScheme| expression + perleval() evaluate Perl expression (|+perl|) py3eval() evaluate Python expression (|+python3|) pyeval() evaluate Python expression (|+python|) wordcount() get byte/word/char count of buffer diff --git a/src/Makefile b/src/Makefile index af18195726..77afbfd672 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1948,7 +1948,7 @@ unittest unittests: $(UNITTEST_TARGETS) ./$$t || exit 1; echo $$t passed; \ done -# Run individual test, assuming that Vim was already compiled. +# Run individual OLD style test, assuming that Vim was already compiled. test1 \ test_argument_0count \ test_argument_count \ @@ -1963,6 +1963,7 @@ test1 \ test_erasebackword \ test_eval \ test_fixeol \ + test_getcwd \ test_insertcount \ test_listchars \ test_listlbr \ @@ -1972,7 +1973,6 @@ test1 \ test_match_conceal \ test_nested_function \ test_options \ - test_perl \ test_qf_title \ test_ruby \ test_search_mbyte \ @@ -1995,7 +1995,9 @@ test1 \ test100 test101 test102 test103 test104 test105 test106 test107 test108: cd testdir; rm -f $@.out; $(MAKE) -f Makefile $@.out VIMPROG=../$(VIMTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) -test_assert \ +# Run individual NEW style test, assuming that Vim was already compiled. +test_arglist \ + test_assert \ test_backspace_opt \ test_cdo \ test_cursor_func \ @@ -2005,6 +2007,7 @@ test_assert \ test_increment \ test_lispwords \ test_menu \ + test_perl \ test_quickfix \ test_searchpos \ test_set \ diff --git a/src/eval.c b/src/eval.c index d38c391488..a9b24ee322 100644 --- a/src/eval.c +++ b/src/eval.c @@ -657,6 +657,9 @@ static void f_nextnonblank __ARGS((typval_T *argvars, typval_T *rettv)); static void f_nr2char __ARGS((typval_T *argvars, typval_T *rettv)); static void f_or __ARGS((typval_T *argvars, typval_T *rettv)); static void f_pathshorten __ARGS((typval_T *argvars, typval_T *rettv)); +#ifdef FEAT_PERL +static void f_perleval __ARGS((typval_T *argvars, typval_T *rettv)); +#endif #ifdef FEAT_FLOAT static void f_pow __ARGS((typval_T *argvars, typval_T *rettv)); #endif @@ -858,6 +861,7 @@ static int can_free_funccal __ARGS((funccall_T *fc, int copyID)) ; static void free_funccal __ARGS((funccall_T *fc, int free_val)); static void add_nr_var __ARGS((dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)); static win_T *find_win_by_nr __ARGS((typval_T *vp, tabpage_T *tp)); +static win_T *find_tabwin __ARGS((typval_T *wvp, typval_T *tvp)); static void getwinvar __ARGS((typval_T *argvars, typval_T *rettv, int off)); static int searchpair_cmn __ARGS((typval_T *argvars, pos_T *match_pos)); static int search_cmn __ARGS((typval_T *argvars, pos_T *match_pos, int *flagsp)); @@ -3684,7 +3688,7 @@ do_unlet_var(lp, name_end, forceit) { listitem_T *li; listitem_T *ll_li = lp->ll_li; - int ll_n1 = lp->ll_n1; + int ll_n1 = lp->ll_n1; while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { @@ -8181,7 +8185,7 @@ static struct fst {"getcmdtype", 0, 0, f_getcmdtype}, {"getcmdwintype", 0, 0, f_getcmdwintype}, {"getcurpos", 0, 0, f_getcurpos}, - {"getcwd", 0, 0, f_getcwd}, + {"getcwd", 0, 2, f_getcwd}, {"getfontname", 0, 1, f_getfontname}, {"getfperm", 1, 1, f_getfperm}, {"getfsize", 1, 1, f_getfsize}, @@ -8205,7 +8209,7 @@ static struct fst {"globpath", 2, 5, f_globpath}, {"has", 1, 1, f_has}, {"has_key", 2, 2, f_has_key}, - {"haslocaldir", 0, 0, f_haslocaldir}, + {"haslocaldir", 0, 2, f_haslocaldir}, {"hasmapto", 1, 3, f_hasmapto}, {"highlightID", 1, 1, f_hlID}, /* obsolete */ {"highlight_exists",1, 1, f_hlexists}, /* obsolete */ @@ -8271,6 +8275,9 @@ static struct fst {"nr2char", 1, 2, f_nr2char}, {"or", 2, 2, f_or}, {"pathshorten", 1, 1, f_pathshorten}, +#ifdef FEAT_PERL + {"perleval", 1, 1, f_perleval}, +#endif #ifdef FEAT_FLOAT {"pow", 2, 2, f_pow}, #endif @@ -9122,30 +9129,11 @@ f_arglistid(argvars, rettv) typval_T *rettv; { win_T *wp; - tabpage_T *tp = NULL; - long n; rettv->vval.v_number = -1; - if (argvars[0].v_type != VAR_UNKNOWN) - { - if (argvars[1].v_type != VAR_UNKNOWN) - { - n = get_tv_number(&argvars[1]); - if (n >= 0) - tp = find_tabpage(n); - } - else - tp = curtab; - - if (tp != NULL) - { - wp = find_win_by_nr(&argvars[0], tp); - if (wp != NULL) - rettv->vval.v_number = wp->w_alist->id; - } - } - else - rettv->vval.v_number = curwin->w_alist->id; + wp = find_tabwin(&argvars[0], &argvars[1]); + if (wp != NULL) + rettv->vval.v_number = wp->w_alist->id; } /* @@ -12063,25 +12051,36 @@ f_getcmdwintype(argvars, rettv) */ static void f_getcwd(argvars, rettv) - typval_T *argvars UNUSED; + typval_T *argvars; typval_T *rettv; { + win_T *wp = NULL; char_u *cwd; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - cwd = alloc(MAXPATHL); - if (cwd != NULL) + + wp = find_tabwin(&argvars[0], &argvars[1]); + if (wp != NULL) { - if (mch_dirname(cwd, MAXPATHL) != FAIL) + if (wp->w_localdir != NULL) + rettv->vval.v_string = vim_strsave(wp->w_localdir); + else if(globaldir != NULL) + rettv->vval.v_string = vim_strsave(globaldir); + else { - rettv->vval.v_string = vim_strsave(cwd); -#ifdef BACKSLASH_IN_FILENAME - if (rettv->vval.v_string != NULL) - slash_adjust(rettv->vval.v_string); -#endif + cwd = alloc(MAXPATHL); + if (cwd != NULL) + { + if (mch_dirname(cwd, MAXPATHL) != FAIL) + rettv->vval.v_string = vim_strsave(cwd); + vim_free(cwd); + } } - vim_free(cwd); +#ifdef BACKSLASH_IN_FILENAME + if (rettv->vval.v_string != NULL) + slash_adjust(rettv->vval.v_string); +#endif } } @@ -12710,6 +12709,38 @@ find_win_by_nr(vp, tp) #endif } +/* + * Find window specified by "wvp" in tabpage "tvp". + */ + static win_T * +find_tabwin(wvp, tvp) + typval_T *wvp; /* VAR_UNKNOWN for current window */ + typval_T *tvp; /* VAR_UNKNOWN for current tab page */ +{ + win_T *wp = NULL; + tabpage_T *tp = NULL; + long n; + + if (wvp->v_type != VAR_UNKNOWN) + { + if (tvp->v_type != VAR_UNKNOWN) + { + n = get_tv_number(tvp); + if (n >= 0) + tp = find_tabpage(n); + } + else + tp = curtab; + + if (tp != NULL) + wp = find_win_by_nr(wvp, tp); + } + else + wp = curwin; + + return wp; +} + /* * "getwinvar()" function */ @@ -13557,10 +13588,13 @@ f_has_key(argvars, rettv) */ static void f_haslocaldir(argvars, rettv) - typval_T *argvars UNUSED; + typval_T *argvars; typval_T *rettv; { - rettv->vval.v_number = (curwin->w_localdir != NULL); + win_T *wp = NULL; + + wp = find_tabwin(&argvars[0], &argvars[1]); + rettv->vval.v_number = (wp != NULL && wp->w_localdir != NULL); } /* @@ -15500,6 +15534,23 @@ f_pathshorten(argvars, rettv) } } +#ifdef FEAT_PERL +/* + * "perleval()" function + */ + static void +f_perleval(argvars, rettv) + typval_T *argvars; + typval_T *rettv; +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + str = get_tv_string_buf(&argvars[0], buf); + do_perleval(str, rettv); +} +#endif + #ifdef FEAT_FLOAT /* * "pow()" function @@ -21856,15 +21907,15 @@ get_funccal() funccal = current_funccal; if (debug_backtrace_level > 0) { - for (i = 0; i < debug_backtrace_level; i++) - { - temp_funccal = funccal->caller; - if (temp_funccal) - funccal = temp_funccal; + for (i = 0; i < debug_backtrace_level; i++) + { + temp_funccal = funccal->caller; + if (temp_funccal) + funccal = temp_funccal; else - /* backtrace level overflow. reset to max */ - debug_backtrace_level = i; - } + /* backtrace level overflow. reset to max */ + debug_backtrace_level = i; + } } return funccal; } @@ -23384,8 +23435,8 @@ ret_free: * Also handles a Funcref in a List or Dictionary. * Returns the function name in allocated memory, or NULL for failure. * flags: - * TFN_INT: internal function name OK - * TFN_QUIET: be quiet + * TFN_INT: internal function name OK + * TFN_QUIET: be quiet * TFN_NO_AUTOLOAD: do not use script autoloading * Advances "pp" to just after the function name (if no error). */ diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index a4074c8583..02d454c3de 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -2909,6 +2909,7 @@ alist_add_list(count, files, after) int after; /* where to add: 0 = before first one */ { int i; + int old_argcount = ARGCOUNT; if (ga_grow(&ALIST(curwin)->al_ga, count) == OK) { @@ -2925,8 +2926,8 @@ alist_add_list(count, files, after) ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED); } ALIST(curwin)->al_ga.ga_len += count; - if (curwin->w_arg_idx >= after) - ++curwin->w_arg_idx; + if (old_argcount > 0 && curwin->w_arg_idx >= after) + curwin->w_arg_idx += count; return after; } diff --git a/src/fileio.c b/src/fileio.c index 5a05836efd..a5907c65e4 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -7335,14 +7335,10 @@ delete_recursive(char_u *name) /* A symbolic link to a directory itself is deleted, not the directory it * points to. */ if ( -# if defined(WIN32) - mch_isdir(name) && !mch_is_symbolic_link(name) -# else -# ifdef UNIX +# if defined(UNIX) || defined(WIN32) mch_isrealdir(name) -# else +# else mch_isdir(name) -# endif # endif ) { diff --git a/src/gui_w48.c b/src/gui_w48.c index ef288e1e64..1f3822be93 100644 --- a/src/gui_w48.c +++ b/src/gui_w48.c @@ -301,18 +301,18 @@ static struct }; /* Local variables */ -static int s_button_pending = -1; +static int s_button_pending = -1; /* s_getting_focus is set when we got focus but didn't see mouse-up event yet, * so don't reset s_button_pending. */ -static int s_getting_focus = FALSE; +static int s_getting_focus = FALSE; -static int s_x_pending; -static int s_y_pending; -static UINT s_kFlags_pending; -static UINT s_wait_timer = 0; /* Timer for get char from user */ -static int s_timed_out = FALSE; -static int dead_key = 0; /* 0 - no dead key, 1 - dead key pressed */ +static int s_x_pending; +static int s_y_pending; +static UINT s_kFlags_pending; +static UINT s_wait_timer = 0; /* Timer for get char from user */ +static int s_timed_out = FALSE; +static int dead_key = 0; /* 0: no dead key, 1: dead key pressed */ #ifdef WIN3264 static OSVERSIONINFO os_version; /* like it says. Init in gui_mch_init() */ @@ -641,6 +641,8 @@ _OnSysChar( int modifiers; int ch = cch; /* special keys are negative */ + dead_key = 0; + /* TRACE("OnSysChar(%d, %c)\n", ch, ch); */ /* OK, we have a character key (given by ch) which was entered with the @@ -1710,6 +1712,34 @@ gui_mch_draw_part_cursor( DeleteBrush(hbr); } + +/* + * Generates a VK_SPACE when the internal dead_key flag is set to output the + * dead key's nominal character and re-post the original message. + */ + static void +outputDeadKey_rePost(MSG originalMsg) +{ + static MSG deadCharExpel; + + if (!dead_key) + return; + + dead_key = 0; + + /* Make Windows generate the dead key's character */ + deadCharExpel.message = originalMsg.message; + deadCharExpel.hwnd = originalMsg.hwnd; + deadCharExpel.wParam = VK_SPACE; + + MyTranslateMessage(&deadCharExpel); + + /* re-generate the current character free of the dead char influence */ + PostMessage(originalMsg.hwnd, originalMsg.message, originalMsg.wParam, + originalMsg.lParam); +} + + /* * Process a single Windows message. * If one is not available we hang until one is. @@ -1790,21 +1820,48 @@ process_message(void) if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) { vk = (int) msg.wParam; + /* - * If a dead key was pressed and the user presses VK_SPACE, VK_BACK, or - * VK_ESCAPE it means that he actually wants to deal with the dead char - * now, so do nothing special and let Windows handle it. + * Handle dead keys in special conditions in other cases we let Windows + * handle them and do not interfere. * - * Note that VK_SPACE combines with the dead_key's character and only - * one WM_CHAR will be generated by TranslateMessage(), in the two - * other cases two WM_CHAR will be generated: the dead char and VK_BACK - * or VK_ESCAPE. That is most likely what the user expects. + * The dead_key flag must be reset on several occasions: + * - in _OnChar() (or _OnSysChar()) as any dead key was necessarily + * consumed at that point (This is when we let Windows combine the + * dead character on its own) + * + * - Before doing something special such as regenerating keypresses to + * expel the dead character as this could trigger an infinite loop if + * for some reason MyTranslateMessage() do not trigger a call + * immediately to _OnChar() (or _OnSysChar()). */ - if (dead_key && (vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE)) + if (dead_key) { - dead_key = 0; - MyTranslateMessage(&msg); - return; + /* + * If a dead key was pressed and the user presses VK_SPACE, + * VK_BACK, or VK_ESCAPE it means that he actually wants to deal + * with the dead char now, so do nothing special and let Windows + * handle it. + * + * Note that VK_SPACE combines with the dead_key's character and + * only one WM_CHAR will be generated by TranslateMessage(), in + * the two other cases two WM_CHAR will be generated: the dead + * char and VK_BACK or VK_ESCAPE. That is most likely what the + * user expects. + */ + if ((vk == VK_SPACE || vk == VK_BACK || vk == VK_ESCAPE)) + { + dead_key = 0; + MyTranslateMessage(&msg); + return; + } + /* In modes where we are not typing, dead keys should behave + * normally */ + else if (!(get_real_state() & (INSERT | CMDLINE | SELECTMODE))) + { + outputDeadKey_rePost(msg); + return; + } } /* Check for CTRL-BREAK */ @@ -1822,6 +1879,19 @@ process_message(void) if (special_keys[i].key_sym == vk && (vk != VK_SPACE || !(GetKeyState(VK_MENU) & 0x8000))) { + /* + * Behave as exected if we have a dead key and the special key + * is a key that would normally trigger the dead key nominal + * character output (such as a NUMPAD printable character or + * the TAB key, etc...). + */ + if (dead_key && (special_keys[i].vim_code0 == 'K' + || vk == VK_TAB || vk == CAR)) + { + outputDeadKey_rePost(msg); + return; + } + #ifdef FEAT_MENU /* Check for : Windows selects the menu. When is * mapped we want to use the mapping instead. */ diff --git a/src/if_perl.xs b/src/if_perl.xs index 098b62e099..840de7d973 100644 --- a/src/if_perl.xs +++ b/src/if_perl.xs @@ -117,7 +117,9 @@ #if (PERL_REVISION == 5) && (PERL_VERSION >= 14) && defined(_MSC_VER) /* Using PL_errgv to get the error message after perl_eval_sv() causes a crash * with MSVC and Perl version 5.14. */ -# define AVOID_PL_ERRGV +# define CHECK_EVAL_ERR(len) SvPV(perl_get_sv("@", GV_ADD), (len)); +#else +# define CHECK_EVAL_ERR(len) SvPV(GvSV(PL_errgv), (len)); #endif /* Compatibility hacks over */ @@ -279,6 +281,13 @@ typedef int perl_key; # define PL_thr_key *dll_PL_thr_key # endif # endif +# define Perl_hv_iternext_flags dll_Perl_hv_iternext_flags +# define Perl_hv_iterinit dll_Perl_hv_iterinit +# define Perl_hv_iterkey dll_Perl_hv_iterkey +# define Perl_hv_iterval dll_Perl_hv_iterval +# define Perl_av_fetch dll_Perl_av_fetch +# define Perl_av_len dll_Perl_av_len +# define Perl_sv_2nv_flags dll_Perl_sv_2nv_flags /* * Declare HANDLE for perl.dll and function pointers. @@ -422,6 +431,13 @@ static SV* (*Perl_Isv_yes_ptr)(register PerlInterpreter*); static perl_key* (*Perl_Gthr_key_ptr)_((pTHX)); #endif static void (*boot_DynaLoader)_((pTHX_ CV*)); +static HE * (*Perl_hv_iternext_flags)(pTHX_ HV *, I32); +static I32 (*Perl_hv_iterinit)(pTHX_ HV *); +static char * (*Perl_hv_iterkey)(pTHX_ HE *, I32 *); +static SV * (*Perl_hv_iterval)(pTHX_ HV *, HE *); +static SV** (*Perl_av_fetch)(pTHX_ AV *, SSize_t, I32); +static SSize_t (*Perl_av_len)(pTHX_ AV *); +static NV (*Perl_sv_2nv_flags)(pTHX_ SV *const, const I32); /* * Table of name to function pointer of perl. @@ -554,6 +570,13 @@ static struct { {"Perl_Gthr_key_ptr", (PERL_PROC*)&Perl_Gthr_key_ptr}, #endif {"boot_DynaLoader", (PERL_PROC*)&boot_DynaLoader}, + {"Perl_hv_iternext_flags", (PERL_PROC*)&Perl_hv_iternext_flags}, + {"Perl_hv_iterinit", (PERL_PROC*)&Perl_hv_iterinit}, + {"Perl_hv_iterkey", (PERL_PROC*)&Perl_hv_iterkey}, + {"Perl_hv_iterval", (PERL_PROC*)&Perl_hv_iterval}, + {"Perl_av_fetch", (PERL_PROC*)&Perl_av_fetch}, + {"Perl_av_len", (PERL_PROC*)&Perl_av_len}, + {"Perl_sv_2nv_flags", (PERL_PROC*)&Perl_sv_2nv_flags}, {"", NULL}, }; @@ -656,7 +679,7 @@ perl_end() perl_free(perl_interp); perl_interp = NULL; #if (PERL_REVISION == 5) && (PERL_VERSION >= 10) - Perl_sys_term(); + Perl_sys_term(); #endif } #ifdef DYNAMIC_PERL @@ -910,11 +933,7 @@ ex_perl(eap) SvREFCNT_dec(sv); -#ifdef AVOID_PL_ERRGV - err = SvPV(perl_get_sv("@", GV_ADD), length); -#else - err = SvPV(GvSV(PL_errgv), length); -#endif + err = CHECK_EVAL_ERR(length); FREETMPS; LEAVE; @@ -949,6 +968,275 @@ replace_line(line, end) return OK; } +static struct ref_map_S { + void *vim_ref; + SV *perl_ref; + struct ref_map_S *next; +} *ref_map = NULL; + + static void +ref_map_free(void) +{ + struct ref_map_S *tofree; + struct ref_map_S *refs = ref_map; + + while (refs) { + tofree = refs; + refs = refs->next; + vim_free(tofree); + } + ref_map = NULL; +} + + static struct ref_map_S * +ref_map_find_SV(sv) + SV *const sv; +{ + struct ref_map_S *refs = ref_map; + int count = 350; + + while (refs) { + if (refs->perl_ref == sv) + break; + refs = refs->next; + count--; + } + + if (!refs && count > 0) { + refs = (struct ref_map_S *)alloc(sizeof(struct ref_map_S)); + if (!refs) + return NULL; + refs->perl_ref = sv; + refs->vim_ref = NULL; + refs->next = ref_map; + ref_map = refs; + } + + return refs; +} + + static int +perl_to_vim(sv, rettv) + SV *sv; + typval_T *rettv; +{ + if (SvROK(sv)) + sv = SvRV(sv); + + switch (SvTYPE(sv)) { + case SVt_NULL: + break; + case SVt_NV: /* float */ +#ifdef FEAT_FLOAT + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = SvNV(sv); + break; +#endif + case SVt_IV: /* integer */ + if (!SvROK(sv)) { /* references should be string */ + rettv->vval.v_number = SvIV(sv); + break; + } + case SVt_PV: /* string */ + { + size_t len = 0; + char * str_from = SvPV(sv, len); + char_u *str_to = (char_u*)alloc(sizeof(char_u) * (len + 1)); + + if (str_to) { + str_to[len] = '\0'; + + while (len--) { + if (str_from[len] == '\0') + str_to[len] = '\n'; + else + str_to[len] = str_from[len]; + } + } + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = str_to; + break; + } + case SVt_PVAV: /* list */ + { + SSize_t size; + listitem_T * item; + SV ** item2; + list_T * list; + struct ref_map_S * refs; + + if ((refs = ref_map_find_SV(sv)) == NULL) + return FAIL; + + if (refs->vim_ref) + list = (list_T *) refs->vim_ref; + else + { + if ((list = list_alloc()) == NULL) + return FAIL; + refs->vim_ref = list; + + for (size = av_len((AV*)sv); size >= 0; size--) + { + if ((item = listitem_alloc()) == NULL) + break; + + item->li_tv.v_type = VAR_NUMBER; + item->li_tv.v_lock = 0; + item->li_tv.vval.v_number = 0; + list_insert(list, item, list->lv_first); + + item2 = av_fetch((AV *)sv, size, 0); + + if (item2 == NULL || *item2 == NULL || + perl_to_vim(*item2, &item->li_tv) == FAIL) + break; + } + } + + list->lv_refcount++; + rettv->v_type = VAR_LIST; + rettv->vval.v_list = list; + break; + } + case SVt_PVHV: /* dictionary */ + { + HE * entry; + size_t key_len; + char * key; + dictitem_T * item; + SV * item2; + dict_T * dict; + struct ref_map_S * refs; + + if ((refs = ref_map_find_SV(sv)) == NULL) + return FAIL; + + if (refs->vim_ref) + dict = (dict_T *) refs->vim_ref; + else + { + + if ((dict = dict_alloc()) == NULL) + return FAIL; + refs->vim_ref = dict; + + hv_iterinit((HV *)sv); + + for (entry = hv_iternext((HV *)sv); entry; entry = hv_iternext((HV *)sv)) + { + key_len = 0; + key = hv_iterkey(entry, (I32 *)&key_len); + + if (!key || !key_len || strlen(key) < key_len) { + EMSG2("Malformed key Dictionary '%s'", key && *key ? key : "(empty)"); + break; + } + + if ((item = dictitem_alloc((char_u *)key)) == NULL) + break; + + item->di_tv.v_type = VAR_NUMBER; + item->di_tv.v_lock = 0; + item->di_tv.vval.v_number = 0; + + if (dict_add(dict, item) == FAIL) { + dictitem_free(item); + break; + } + item2 = hv_iterval((HV *)sv, entry); + if (item2 == NULL || perl_to_vim(item2, &item->di_tv) == FAIL) + break; + } + } + + dict->dv_refcount++; + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = dict; + break; + } + default: /* not convertible */ + { + char *val = SvPV_nolen(sv); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = val ? vim_strsave((char_u *)val) : NULL; + break; + } + } + return OK; +} + +/* + * "perleval()" + */ + void +do_perleval(str, rettv) + char_u *str; + typval_T *rettv; +{ + char *err = NULL; + STRLEN err_len = 0; + SV *sv = NULL; +#ifdef HAVE_SANDBOX + SV *safe; +#endif + + if (perl_interp == NULL) + { +#ifdef DYNAMIC_PERL + if (!perl_enabled(TRUE)) + { + EMSG(_(e_noperl)); + return; + } +#endif + perl_init(); + } + + { + dSP; + ENTER; + SAVETMPS; + +#ifdef HAVE_SANDBOX + if (sandbox) + { + safe = get_sv("VIM::safe", FALSE); +# ifndef MAKE_TEST /* avoid a warning for unreachable code */ + if (safe == NULL || !SvTRUE(safe)) + EMSG(_("E299: Perl evaluation forbidden in sandbox without the Safe module")); + else +# endif + { + sv = newSVpv((char *)str, 0); + PUSHMARK(SP); + XPUSHs(safe); + XPUSHs(sv); + PUTBACK; + call_method("reval", G_SCALAR); + SPAGAIN; + SvREFCNT_dec(sv); + sv = POPs; + } + } + else +#endif /* HAVE_SANDBOX */ + sv = eval_pv((char *)str, 0); + + if (sv) { + perl_to_vim(sv, rettv); + ref_map_free(); + err = CHECK_EVAL_ERR(err_len); + } + PUTBACK; + FREETMPS; + LEAVE; + } + if (err_len) + msg_split((char_u *)err, highlight_attr[HLF_E]); +} + /* * ":perldo". */ @@ -984,11 +1272,7 @@ ex_perldo(eap) sv_catpvn(sv, "}", 1); perl_eval_sv(sv, G_DISCARD | G_NOARGS); SvREFCNT_dec(sv); -#ifdef AVOID_PL_ERRGV - str = SvPV(perl_get_sv("@", GV_ADD), length); -#else - str = SvPV(GvSV(PL_errgv), length); -#endif + str = CHECK_EVAL_ERR(length); if (length) goto err; @@ -1002,11 +1286,7 @@ ex_perldo(eap) sv_setpv(GvSV(PL_defgv), (char *)ml_get(i)); PUSHMARK(sp); perl_call_pv("VIM::perldo", G_SCALAR | G_EVAL); -#ifdef AVOID_PL_ERRGV - str = SvPV(perl_get_sv("@", GV_ADD), length); -#else - str = SvPV(GvSV(PL_errgv), length); -#endif + str = CHECK_EVAL_ERR(length); if (length) break; SPAGAIN; diff --git a/src/if_py_both.h b/src/if_py_both.h index 1ad7dc616d..e220e3fec7 100644 --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -5521,7 +5521,7 @@ run_eval(const char *cmd, typval_T *rettv } else { - if (ConvertFromPyObject(run_ret, rettv) == -1) + if (run_ret != Py_None && ConvertFromPyObject(run_ret, rettv) == -1) EMSG(_("E859: Failed to convert returned python object to vim value")); Py_DECREF(run_ret); } diff --git a/src/os_win32.c b/src/os_win32.c index a47ffaf18a..b4f5fa4666 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -3129,6 +3129,17 @@ mch_isdir(char_u *name) return (f & FILE_ATTRIBUTE_DIRECTORY) != 0; } +/* + * return TRUE if "name" is a directory, NOT a symlink to a directory + * return FALSE if "name" is not a directory + * return FALSE for error + */ + int +mch_isrealdir(char_u *name) +{ + return mch_isdir(name) && !mch_is_symbolic_link(name); +} + /* * Create directory "name". * Return 0 on success, -1 on error. @@ -3190,10 +3201,10 @@ mch_is_hard_link(char_u *fname) } /* - * Return TRUE if file "fname" is a symbolic link. + * Return TRUE if "name" is a symbolic link (or a junction). */ int -mch_is_symbolic_link(char_u *fname) +mch_is_symbolic_link(char_u *name) { HANDLE hFind; int res = FALSE; @@ -3204,7 +3215,7 @@ mch_is_symbolic_link(char_u *fname) WIN32_FIND_DATAW findDataW; if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) - wn = enc_to_utf16(fname, NULL); + wn = enc_to_utf16(name, NULL); if (wn != NULL) { hFind = FindFirstFileW(wn, &findDataW); @@ -3213,7 +3224,7 @@ mch_is_symbolic_link(char_u *fname) && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) { /* Retry with non-wide function (for Windows 98). */ - hFind = FindFirstFile(fname, &findDataA); + hFind = FindFirstFile(name, &findDataA); if (hFind != INVALID_HANDLE_VALUE) { fileFlags = findDataA.dwFileAttributes; @@ -3229,7 +3240,7 @@ mch_is_symbolic_link(char_u *fname) else #endif { - hFind = FindFirstFile(fname, &findDataA); + hFind = FindFirstFile(name, &findDataA); if (hFind != INVALID_HANDLE_VALUE) { fileFlags = findDataA.dwFileAttributes; @@ -3241,7 +3252,8 @@ mch_is_symbolic_link(char_u *fname) FindClose(hFind); if ((fileFlags & FILE_ATTRIBUTE_REPARSE_POINT) - && reparseTag == IO_REPARSE_TAG_SYMLINK) + && (reparseTag == IO_REPARSE_TAG_SYMLINK + || reparseTag == IO_REPARSE_TAG_MOUNT_POINT)) res = TRUE; return res; @@ -5839,7 +5851,8 @@ mch_delay( /* - * this version of remove is not scared by a readonly (backup) file + * This version of remove is not scared by a readonly (backup) file. + * This can also remove a symbolic link like Unix. * Return 0 for success, -1 for failure. */ int @@ -5850,6 +5863,13 @@ mch_remove(char_u *name) int n; #endif + /* + * On Windows, deleting a directory's symbolic link is done by + * RemoveDirectory(): mch_rmdir. It seems unnatural, but it is fact. + */ + if (mch_isdir(name) && mch_is_symbolic_link(name)) + return mch_rmdir(name); + win32_setattrs(name, FILE_ATTRIBUTE_NORMAL); #ifdef FEAT_MBYTE diff --git a/src/proto/if_perl.pro b/src/proto/if_perl.pro index fe0301b0b0..3272814759 100644 --- a/src/proto/if_perl.pro +++ b/src/proto/if_perl.pro @@ -6,3 +6,4 @@ void perl_win_free __ARGS((win_T *wp)); void perl_buf_free __ARGS((buf_T *bp)); void ex_perl __ARGS((exarg_T *eap)); void ex_perldo __ARGS((exarg_T *eap)); +void do_perleval __ARGS((char_u *str, typval_T *rettv)); diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro index 7cdd15677a..b3acb4f4cb 100644 --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -21,6 +21,7 @@ int mch_setperm __ARGS((char_u *name, long perm)); void mch_hide __ARGS((char_u *name)); int mch_ishidden __ARGS((char_u *name)); int mch_isdir __ARGS((char_u *name)); +int mch_isrealdir __ARGS((char_u *name)); int mch_mkdir __ARGS((char_u *name)); int mch_rmdir __ARGS((char_u *name)); int mch_is_hard_link __ARGS((char_u *fname)); diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 69fd936cd1..33d76ba49e 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -103,6 +103,7 @@ SCRIPTS_ALL = \ test_erasebackword.out \ test_eval.out \ test_fixeol.out \ + test_getcwd.out \ test_insertcount.out \ test_listchars.out \ test_listlbr.out \ @@ -112,7 +113,6 @@ SCRIPTS_ALL = \ test_match_conceal.out \ test_nested_function.out \ test_options.out \ - test_perl.out \ test_qf_title.out \ test_ruby.out \ test_search_mbyte.out \ @@ -178,7 +178,8 @@ NEW_TESTS = test_arglist.res \ test_increment.res \ test_quickfix.res \ test_viml.res \ - test_alot.res + test_alot.res \ + test_perl.res # Explicit dependencies. diff --git a/src/testdir/test86.in b/src/testdir/test86.in index 4714451eeb..cc76cff8d4 100644 --- a/src/testdir/test86.in +++ b/src/testdir/test86.in @@ -329,6 +329,8 @@ EOF :$put =string(l) :let d=pyeval('{"a": "b", "c": 1, "d": ["e"]}') :$put =sort(items(d)) +:let v:errmsg = '' +:$put ='pyeval(\"None\") = ' . pyeval('None') . v:errmsg :if has('float') : let f=pyeval('0.0') : $put =string(f) diff --git a/src/testdir/test86.ok b/src/testdir/test86.ok index 5cb0ac5740..d103909ca1 100644 --- a/src/testdir/test86.ok +++ b/src/testdir/test86.ok @@ -86,6 +86,7 @@ ll:[1] ['a', 'b'] ['c', 1] ['d', ['e']] +pyeval("None") = 0 0.0 "\0": Vim(let):E859: {"\0": 1}: Vim(let):E859: diff --git a/src/testdir/test87.in b/src/testdir/test87.in index 41d406521e..154e823827 100644 --- a/src/testdir/test87.in +++ b/src/testdir/test87.in @@ -326,6 +326,8 @@ EOF :$put =string(l) :let d=py3eval('{"a": "b", "c": 1, "d": ["e"]}') :$put =sort(items(d)) +:let v:errmsg = '' +:$put ='py3eval(\"None\") = ' . py3eval('None') . v:errmsg :if has('float') : let f=py3eval('0.0') : $put =string(f) diff --git a/src/testdir/test87.ok b/src/testdir/test87.ok index c410356065..65bd596542 100644 --- a/src/testdir/test87.ok +++ b/src/testdir/test87.ok @@ -86,6 +86,7 @@ ll:[1] ['a', 'b'] ['c', 1] ['d', ['e']] +py3eval("None") = 0 0.0 "\0": Vim(let):E859: {"\0": 1}: Vim(let):E859: diff --git a/src/testdir/test_arglist.vim b/src/testdir/test_arglist.vim index 3f72f0ff9c..522631741a 100644 --- a/src/testdir/test_arglist.vim +++ b/src/testdir/test_arglist.vim @@ -20,3 +20,55 @@ func Test_argidx() 1argdelete call assert_equal(0, argidx()) endfunc + +func Test_argadd() + %argdelete + argadd a b c + call assert_equal(0, argidx()) + + %argdelete + argadd a + call assert_equal(0, argidx()) + argadd b c d + call assert_equal(0, argidx()) + + call Init_abc() + argadd x + call Assert_argc(['a', 'b', 'x', 'c']) + call assert_equal(1, argidx()) + + call Init_abc() + 0argadd x + call Assert_argc(['x', 'a', 'b', 'c']) + call assert_equal(2, argidx()) + + call Init_abc() + 1argadd x + call Assert_argc(['a', 'x', 'b', 'c']) + call assert_equal(2, argidx()) + + call Init_abc() + $argadd x + call Assert_argc(['a', 'b', 'c', 'x']) + call assert_equal(1, argidx()) + + call Init_abc() + $argadd x + +2argadd y + call Assert_argc(['a', 'b', 'c', 'x', 'y']) + call assert_equal(1, argidx()) +endfunc + +func Init_abc() + args a b c + next +endfunc + +func Assert_argc(l) + call assert_equal(len(a:l), argc()) + let i = 0 + while i < len(a:l) && i < argc() + call assert_equal(a:l[i], argv(i)) + let i += 1 + endwhile +endfunc diff --git a/src/testdir/test_getcwd.in b/src/testdir/test_getcwd.in new file mode 100644 index 0000000000..8c7b24ebf0 --- /dev/null +++ b/src/testdir/test_getcwd.in @@ -0,0 +1,101 @@ +Tests for getcwd(), haslocaldir(), and :lcd vim: set ft=vim : + +STARTTEST +:so small.vim +:" Do all test in a separate window to avoid E211 when we recursively +:" delete the Xtopdir directory during cleanup +:" +:" This will cause a few errors, do it silently. +:set visualbell +:set nocp viminfo+=nviminfo +:" +:function! DeleteDirectory(dir) +: if has("win16") || has("win32") || has("win64") || has("dos16") || has("dos32") +: exec "silent !rmdir /Q /S " . a:dir +: else +: exec "silent !rm -rf " . a:dir +: endif +:endfun +:" +:function! GetCwdInfo(win, tab) +: let tab_changed = 0 +: let mod = ":t" +: if a:tab > 0 && a:tab != tabpagenr() +: let tab_changed = 1 +: exec "tabnext " . a:tab +: endif +: let bufname = fnamemodify(bufname(winbufnr(a:win)), mod) +: if tab_changed +: tabprevious +: endif +: if a:win == 0 && a:tab == 0 +: let dirname = fnamemodify(getcwd(), mod) +: let lflag = haslocaldir() +: elseif a:tab == 0 +: let dirname = fnamemodify(getcwd(a:win), mod) +: let lflag = haslocaldir(a:win) +: else +: let dirname = fnamemodify(getcwd(a:win, a:tab), mod) +: let lflag = haslocaldir(a:win, a:tab) +: endif +: return bufname . ' ' . dirname . ' ' . lflag +:endfunction +:" On windows a stale "Xtopdir" directory may exist, remove it so that +:" we start from a clean state. +:call DeleteDirectory("Xtopdir") +:let r=[] +:new +:let cwd=getcwd() +:let test_out = cwd . '/test.out' +:call mkdir('Xtopdir') +:cd Xtopdir +:call mkdir('Xdir1') +:call mkdir('Xdir2') +:call mkdir('Xdir3') +:new a +:new b +:new c +:3wincmd w +:lcd Xdir1 +:call add(r, GetCwdInfo(0, 0)) +:wincmd W +:call add(r, GetCwdInfo(0, 0)) +:wincmd W +:lcd Xdir3 +:call add(r, GetCwdInfo(0, 0)) +:call add(r, GetCwdInfo(bufwinnr("a"), 0)) +:call add(r, GetCwdInfo(bufwinnr("b"), 0)) +:call add(r, GetCwdInfo(bufwinnr("c"), 0)) +:wincmd W +:call add(r, GetCwdInfo(bufwinnr("a"), tabpagenr())) +:call add(r, GetCwdInfo(bufwinnr("b"), tabpagenr())) +:call add(r, GetCwdInfo(bufwinnr("c"), tabpagenr())) +:" +:tabnew x +:new y +:new z +:3wincmd w +:call add(r, GetCwdInfo(0, 0)) +:wincmd W +:lcd Xdir2 +:call add(r, GetCwdInfo(0, 0)) +:wincmd W +:lcd Xdir3 +:call add(r, GetCwdInfo(0, 0)) +:call add(r, GetCwdInfo(bufwinnr("x"), 0)) +:call add(r, GetCwdInfo(bufwinnr("y"), 0)) +:call add(r, GetCwdInfo(bufwinnr("z"), 0)) +:let tp_nr = tabpagenr() +:tabrewind +:call add(r, GetCwdInfo(3, tp_nr)) +:call add(r, GetCwdInfo(2, tp_nr)) +:call add(r, GetCwdInfo(1, tp_nr)) +:" +:call writefile(r, test_out, "a") +:q +:exec "cd " . cwd +:call DeleteDirectory("Xtopdir") +:qa! +ENDTEST + + diff --git a/src/testdir/test_getcwd.ok b/src/testdir/test_getcwd.ok new file mode 100644 index 0000000000..23699891f3 --- /dev/null +++ b/src/testdir/test_getcwd.ok @@ -0,0 +1,18 @@ +a Xdir1 1 +b Xtopdir 0 +c Xdir3 1 +a Xdir1 1 +b Xtopdir 0 +c Xdir3 1 +a Xdir1 1 +b Xtopdir 0 +c Xdir3 1 +x Xtopdir 0 +y Xdir2 1 +z Xdir3 1 +x Xtopdir 0 +y Xdir2 1 +z Xdir3 1 +x Xtopdir 0 +y Xdir2 1 +z Xdir3 1 diff --git a/src/testdir/test_perl.in b/src/testdir/test_perl.in deleted file mode 100644 index c028606eb8..0000000000 --- a/src/testdir/test_perl.in +++ /dev/null @@ -1,26 +0,0 @@ -Tests for perl interface. vim: set ft=vim : - -STARTTEST -:so small.vim -:set nocompatible viminfo+=nviminfo -:if !has('perl') | e! test.ok | wq! test.out | endif -:" change buffer contents -:perl VIM::DoCommand("normal /^1\n") -:perl $curline = VIM::Eval("line('.')") -:perl $curbuf->Set($curline, "1 changed line 1") -:" evaluate a List -:perl VIM::DoCommand("normal /^2\n") -:perl $curline = VIM::Eval("line('.')") -:let l = ["abc", "def"] -:perl << EOF -$l = VIM::Eval("l"); -$curbuf->Append($curline, $l); -EOF -:normal j -:.perldo s|\n|/|g -:?^1?,$w! test.out -:qa! -ENDTEST - -1 line 1 -2 line 2 diff --git a/src/testdir/test_perl.ok b/src/testdir/test_perl.ok deleted file mode 100644 index 432cdd9cb0..0000000000 --- a/src/testdir/test_perl.ok +++ /dev/null @@ -1,3 +0,0 @@ -1 changed line 1 -2 line 2 -abc/def/ diff --git a/src/testdir/test_perl.vim b/src/testdir/test_perl.vim new file mode 100644 index 0000000000..79e24f4ac8 --- /dev/null +++ b/src/testdir/test_perl.vim @@ -0,0 +1,94 @@ +" Tests for Perl interface + +if !has('perl') + finish +end + +func Test_change_buffer() + call setline(line('$'), ['1 line 1']) + perl VIM::DoCommand("normal /^1\n") + perl $curline = VIM::Eval("line('.')") + perl $curbuf->Set($curline, "1 changed line 1") + call assert_equal('1 changed line 1', getline('$')) +endfunc + +func Test_evaluate_list() + call setline(line('$'), ['2 line 2']) + perl VIM::DoCommand("normal /^2\n") + perl $curline = VIM::Eval("line('.')") + let l = ["abc", "def"] + perl << EOF + $l = VIM::Eval("l"); + $curbuf->Append($curline, $l); +EOF + normal j + .perldo s|\n|/|g + call assert_equal('abc/def/', getline('$')) +endfunc + +fu catch_peval(expr) + try + call perleval(a:expr) + catch + return v:exception + endtry + call assert_true(0, 'no exception for `perleval("'.a:expr.'")`') + return '' +endf + +function Test_perleval() + call assert_false(perleval('undef')) + + " scalar + call assert_equal(0, perleval('0')) + call assert_equal(2, perleval('2')) + call assert_equal(-2, perleval('-2')) + if has('float') + call assert_equal(2.5, perleval('2.5')) + else + call assert_equal(2, perleval('2.5')) + end + + sandbox call assert_equal(2, perleval('2')) + + call assert_equal('abc', perleval('"abc"')) + call assert_equal("abc\ndef", perleval('"abc\0def"')) + + " ref + call assert_equal([], perleval('[]')) + call assert_equal(['word', 42, [42],{}], perleval('["word", 42, [42], {}]')) + + call assert_equal({}, perleval('{}')) + call assert_equal({'foo': 'bar'}, perleval('{foo => "bar"}')) + + perl our %h; our @a; + let a = perleval('[\(%h, %h, @a, @a)]') + call assert_true((a[0] is a[1])) + call assert_true((a[2] is a[3])) + perl undef %h; undef @a; + + call assert_true(catch_peval('{"" , 0}') =~ 'Malformed key Dictionary') + call assert_true(catch_peval('{"\0" , 0}') =~ 'Malformed key Dictionary') + call assert_true(catch_peval('{"foo\0bar" , 0}') =~ 'Malformed key Dictionary') + + call assert_equal('*VIM', perleval('"*VIM"')) + call assert_true(perleval('\\0') =~ 'SCALAR(0x\x\+)') +endf + +function Test_perldo() + sp __TEST__ + exe 'read ' g:testname + perldo s/perl/vieux_chameau/g + 1 + call assert_false(search('\Cperl')) + bw! +endf + +function Test_VIM_package() + perl VIM::DoCommand('let l:var = "foo"') + call assert_equal(l:var, 'foo') + + set noet + perl VIM::SetOption('et') + call assert_true(&et) +endf diff --git a/src/version.c b/src/version.c index 1bc2e76fd7..871d54aa2e 100644 --- a/src/version.c +++ b/src/version.c @@ -756,6 +756,20 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1129, +/**/ + 1128, +/**/ + 1127, +/**/ + 1126, +/**/ + 1125, +/**/ + 1124, +/**/ + 1123, /**/ 1122, /**/