diff --git a/Filelist b/Filelist index 04a433291e..82fb376e1e 100644 --- a/Filelist +++ b/Filelist @@ -13,7 +13,8 @@ SRC_ALL = \ .hgignore \ .lgtm.yml \ .travis.yml \ - appveyor.yml \ + .appveyor.yml \ + .codecov.yml \ ci/appveyor.bat \ ci/build-snd-dummy.sh \ ci/config.mk*.sed \ diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 86913e3fa9..c7d83ccb54 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1,4 +1,4 @@ -*change.txt* For Vim version 8.2. Last change: 2021 Jan 21 +*change.txt* For Vim version 8.2. Last change: 2021 Mar 01 VIM REFERENCE MANUAL by Bram Moolenaar @@ -649,6 +649,8 @@ For other systems the tmpnam() library function is used. The space between `:substitute` and the 'c', 'g', 'i', 'I' and 'r' flags isn't required, but in scripts it's a good idea to keep it to avoid confusion. + Also see the two and three letter commands to repeat + :substitute below |:substitute-repeat|. :[range]~[&][flags] [count] *:~* Repeat last substitute with same substitute string @@ -877,20 +879,26 @@ either the first or second pattern in parentheses did not match, so either *:sge* *:sgi* *:sgI* *:sgl* *:sgn* *:sgp* *:sgr* *:sI* *:si* *:sic* *:sIc* *:sie* *:sIe* *:sIg* *:sIl* *:sin* *:sIn* *:sIp* *:sip* *:sIr* *:sir* *:sr* *:src* *:srg* *:sri* *:srI* *:srl* - *:srn* *:srp* + *:srn* *:srp* *:substitute-repeat* 2-letter and 3-letter :substitute commands ~ +These commands repeat the previous `:substitute` command with the given flags. +The first letter is always "s", followed by one or two of the possible flag +characters. For example `:sce` works like `:s///ce`. The table lists the +possible combinations, not all flags are possible, because the command is +short for another command. + List of :substitute commands | c e g i I n p l r - | c :sc :sce :scg :sci :scI :scn :scp :scl --- + | c :sc :sce :scg :sci :scI :scn :scp :scl | e | g :sgc :sge :sg :sgi :sgI :sgn :sgp :sgl :sgr - | i :sic :sie --- :si :siI :sin :sip --- :sir + | i :sic :sie :si :siI :sin :sip :sir | I :sIc :sIe :sIg :sIi :sI :sIn :sIp :sIl :sIr | n | p | l - | r :src --- :srg :sri :srI :srn :srp :srl :sr + | r :src :srg :sri :srI :srn :srp :srl :sr Exceptions: :scr is `:scriptnames` diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e5562a32f5..fcfa04ae92 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 8.2. Last change: 2021 Feb 10 +*eval.txt* For Vim version 8.2. Last change: 2021 Mar 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1187,7 +1187,8 @@ byte under the cursor: > In Vim9 script: If expr8 is a String this results in a String that contains the expr1'th -single character from expr8. To use byte indexes use |strpart()|. +single character (including any composing characters) from expr8. To use byte +indexes use |strpart()|. Index zero gives the first byte or character. Careful: text column numbers start with one! @@ -1217,8 +1218,9 @@ In legacy Vim script the indexes are byte indexes. This doesn't recognize multibyte encodings, see |byteidx()| for computing the indexes. If expr8 is a Number it is first converted to a String. -In Vim9 script the indexes are character indexes. To use byte indexes use -|strpart()|. +In Vim9 script the indexes are character indexes and include composing +characters. To use byte indexes use |strpart()|. To use character indexes +without including composing characters use |strcharpart()|. The item at index expr1b is included, it is inclusive. For an exclusive index use the |slice()| function. @@ -2935,10 +2937,11 @@ str2list({expr} [, {utf8}]) List convert each character of {expr} to ASCII/UTF8 value str2nr({expr} [, {base} [, {quoted}]]) Number convert String to Number -strcharpart({str}, {start} [, {len}]) +strcharlen({expr}) Number character length of the String {expr} +strcharpart({str}, {start} [, {len} [, {skipcc}]]) String {len} characters of {str} at character {start} -strchars({expr} [, {skipcc}]) Number character length of the String {expr} +strchars({expr} [, {skipcc}]) Number character count of the String {expr} strdisplaywidth({expr} [, {col}]) Number display length of the String {expr} strftime({format} [, {time}]) String format time with a specified format strgetchar({str}, {index}) Number get char {index} from {str} @@ -5312,6 +5315,9 @@ getcharpos({expr}) Get the position for {expr}. Same as |getpos()| but the column number in the returned List is a character index instead of a byte index. + If |getpos()| returns a very large column number, such as + 2147483647, then getcharpos() will return the character index + of the last character. Example: With the cursor on '세' in line 5 with text "여보세요": > @@ -5791,6 +5797,8 @@ getpos({expr}) Get the position for {expr}. For possible values of {expr} The column number in the returned List is the byte position within the line. To get the character position in the line, use |getcharpos()| + The column number can be very large, e.g. 2147483647, in which + case it means "after the end of the line". This can be used to save and restore the position of a mark: > let save_a_mark = getpos("'a") ... @@ -7462,7 +7470,8 @@ matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]]) to be used when fast match additions and deletions are required, for example to highlight matching parentheses. - The list {pos} can contain one of these items: + {pos} is a list of positions. Each position can be one of + these: - A number. This whole line will be highlighted. The first line has number 1. - A list with one number, e.g., [23]. The whole line with this @@ -7475,7 +7484,7 @@ matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]]) - A list with three numbers, e.g., [23, 11, 3]. As above, but the third number gives the length of the highlight in bytes. - The maximum number of positions is 8. + The maximum number of positions in {pos} is 8. Example: > :highlight MyGroup ctermbg=green guibg=green @@ -7484,8 +7493,7 @@ matchaddpos({group}, {pos} [, {priority} [, {id} [, {dict}]]]) :call matchdelete(m) < Matches added by |matchaddpos()| are returned by - |getmatches()| with an entry "pos1", "pos2", etc., with the - value a list like the {pos} item. + |getmatches()|. Can also be used as a |method|: > GetGroup()->matchaddpos([23, 11]) @@ -9925,7 +9933,7 @@ slice({expr}, {start} [, {end}]) *slice()* Similar to using a |slice| "expr[start : end]", but "end" is used exclusive. And for a string the indexes are used as character indexes instead of byte indexes, like in - |vim9script|. + |vim9script|. Also, composing characters are not counted. When {end} is omitted the slice continues to the last item. When {end} is -1 the last item is omitted. @@ -10283,23 +10291,44 @@ str2nr({expr} [, {base} [, {quoted}]]) *str2nr()* Can also be used as a |method|: > GetText()->str2nr() -strcharpart({src}, {start} [, {len}]) *strcharpart()* + +strcharlen({expr}) *strcharlen()* + The result is a Number, which is the number of characters + in String {expr}. Composing characters are ignored. + |strchars()| can count the number of characters, counting + composing characters separately. + + Also see |strlen()|, |strdisplaywidth()| and |strwidth()|. + + Can also be used as a |method|: > + GetText()->strcharlen() + + +strcharpart({src}, {start} [, {len} [, {skipcc}]]) *strcharpart()* Like |strpart()| but using character index and length instead of byte index and length. + When {skipcc} is omitted or zero, composing characters are + counted separately. + When {skipcc} set to 1, Composing characters are ignored, + similar to |slice()|. When a character index is used where a character does not - exist it is assumed to be one character. For example: > + exist it is omitted and counted as one character. For + example: > strcharpart('abc', -1, 2) < results in 'a'. Can also be used as a |method|: > GetText()->strcharpart(5) + strchars({expr} [, {skipcc}]) *strchars()* The result is a Number, which is the number of characters in String {expr}. When {skipcc} is omitted or zero, composing characters are counted separately. When {skipcc} set to 1, Composing characters are ignored. + |strcharlen()| always does this. + Also see |strlen()|, |strdisplaywidth()| and |strwidth()|. {skipcc} is only available after 7.4.755. For backward @@ -13159,7 +13188,7 @@ text... Cannot be followed by a comment. Examples: > :execute "buffer" nextbuf - :execute "normal" count . "w" + :execute "normal" count .. "w" < ":execute" can be used to append a command to commands that don't accept a '|'. Example: > @@ -13175,8 +13204,8 @@ text... file names. The |fnameescape()| function can be used for Vim commands, |shellescape()| for |:!| commands. Examples: > - :execute "e " . fnameescape(filename) - :execute "!ls " . shellescape(filename, 1) + :execute "e " .. fnameescape(filename) + :execute "!ls " .. shellescape(filename, 1) < Note: The executed string may be any command-line, but starting or ending "if", "while" and "for" does not diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 6a23b2fdb8..318507c16b 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -1,4 +1,4 @@ -*filetype.txt* For Vim version 8.2. Last change: 2021 Jan 21 +*filetype.txt* For Vim version 8.2. Last change: 2021 Mar 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -628,6 +628,13 @@ For fish, add to the config file set -x MANPAGER "vim -M +MANPAGER -" + +MARKDOWN *ft-markdown-plugin* + +To enable folding use this: > + let g:markdown_folding = 1 +< + PDF *ft-pdf-plugin* Two maps, and , are provided to simulate a tag stack for navigating diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index bf391305dc..873d5d6d7a 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -263,6 +263,20 @@ input. Example: > endfunc nnoremap OpenPopup() +Also, keep in mind that the expression may be evaluated when looking for +typeahead, before the previous command has been executed. For example: > + func StoreColumn() + let g:column = col('.') + return 'x' + endfunc + nnoremap x StoreColumn() + nmap ! f!x +You will notice that g:column has the value from before executing "fx", +because "z" is evaluated before "fx" is executed. +This can be solved by inserting before the character that is +expression-mapped: > + nmap ! f!x + Be very careful about side effects! The expression is evaluated while obtaining characters, you may very well make the command dysfunctional. For this reason the following is blocked: diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index aa00dd3b54..71b4e10de4 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -677,9 +677,9 @@ Your directory layout would be like this: opt/fooextra/doc/tags " help tags This allows for the user to do: > - mkdir ~/.vim/pack/myfoobar - cd ~/.vim/pack/myfoobar - git clone https://github.com/you/foobar.git + mkdir ~/.vim/pack + cd ~/.vim/pack + git clone https://github.com/you/foobar.git myfoobar Here "myfoobar" is a name that the user can choose, the only condition is that it differs from other packages. diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index 9c52587f42..66f8cc61bd 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -1,4 +1,4 @@ -*sign.txt* For Vim version 8.2. Last change: 2020 Oct 28 +*sign.txt* For Vim version 8.2. Last change: 2021 Mar 07 VIM REFERENCE MANUAL by Gordon Prieur @@ -146,6 +146,9 @@ See |sign_define()| for the equivalent Vim script function. texthl={group} Highlighting group used for the text item. + Example: > + :sign define MySign text=>> texthl=Search linehl=DiffText +< DELETING A SIGN *:sign-undefine* *E155* @@ -155,7 +158,9 @@ See |sign_undefine()| for the equivalent Vim script function. Deletes a previously defined sign. If signs with this {name} are still placed this will cause trouble. - + Example: > + :sign undefine MySign +< LISTING SIGNS *:sign-list* *E156* @@ -209,6 +214,10 @@ See |sign_place()| for the equivalent Vim script function. Same, but use buffer {nr}. If the buffer argument is not given, place the sign in the current buffer. + Example: > + :sign place 10 line=99 name=sign3 + :sign place 10 line=99 name=sign3 buffer=3 +< *E885* :sign place {id} name={name} file={fname} Change the placed sign {id} in file {fname} to use the defined @@ -221,10 +230,17 @@ See |sign_place()| for the equivalent Vim script function. "priority={prio}" attribute can be used to change the priority of an existing sign. + Example: > + :sign place 23 name=sign1 file=/path/to/edit.py +< :sign place {id} name={name} [buffer={nr}] Same, but use buffer {nr}. If the buffer argument is not given, use the current buffer. + Example: > + :sign place 23 name=sign1 + :sign place 23 name=sign1 buffer=7 +< REMOVING SIGNS *:sign-unplace* *E159* diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 6717e382b1..79557dcc76 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 8.2. Last change: 2021 Jan 21 +*syntax.txt* For Vim version 8.2. Last change: 2021 Mar 06 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1741,8 +1741,8 @@ The coloring scheme for tags in the HTML file works as follows. The <> of opening tags are colored differently than the of a closing tag. This is on purpose! For opening tags the 'Function' color is used, while for -closing tags the 'Type' color is used (See syntax.vim to check how those are -defined for you) +closing tags the 'Identifier' color is used (See syntax.vim to check how those +are defined for you) Known tag names are colored the same way as statements in C. Unknown tag names are colored with the same color as the <> or respectively which @@ -4676,7 +4676,7 @@ matches, nextgroup, etc. But there are a few differences: - A line continuation pattern can be given. It is used to decide which group of lines need to be searched like they were one line. This means that the search for a match with the specified items starts in the first of the - consecutive that contain the continuation pattern. + consecutive lines that contain the continuation pattern. - When using "nextgroup" or "contains", this only works within one line (or group of continued lines). - When using a region, it must start and end in the same line (or group of diff --git a/runtime/doc/tags b/runtime/doc/tags index 55c11989d7..d53784fc91 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -3218,6 +3218,7 @@ $VIM_POSIX vi_diff.txt /*$VIM_POSIX* :stselect tagsrch.txt /*:stselect* :su change.txt /*:su* :substitute change.txt /*:substitute* +:substitute-repeat change.txt /*:substitute-repeat* :sun windows.txt /*:sun* :sunhide windows.txt /*:sunhide* :sunm map.txt /*:sunm* @@ -6667,6 +6668,7 @@ ft-mail.vim syntax.txt /*ft-mail.vim* ft-make-syntax syntax.txt /*ft-make-syntax* ft-man-plugin filetype.txt /*ft-man-plugin* ft-maple-syntax syntax.txt /*ft-maple-syntax* +ft-markdown-plugin filetype.txt /*ft-markdown-plugin* ft-masm-syntax syntax.txt /*ft-masm-syntax* ft-mathematica-syntax syntax.txt /*ft-mathematica-syntax* ft-matlab-indent indent.txt /*ft-matlab-indent* diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt index 5be1890df8..4e4cff0c27 100644 --- a/runtime/doc/testing.txt +++ b/runtime/doc/testing.txt @@ -1,4 +1,4 @@ -*testing.txt* For Vim version 8.2. Last change: 2020 Dec 12 +*testing.txt* For Vim version 8.2. Last change: 2021 Mar 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -168,6 +168,7 @@ test_override({name}, {val}) *test_override()* wait time of up to 3 seconds for messages term_props reset all terminal properties when the version string is detected + uptime overrules sysinfo.uptime ALL clear all overrides ({val} is not used) "starting" is to be used when a test should behave like diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index 835a5ccca1..2486d9d50f 100644 --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1,4 +1,4 @@ -*todo.txt* For Vim version 8.2. Last change: 2021 Feb 20 +*todo.txt* For Vim version 8.2. Last change: 2021 Mar 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -39,13 +39,25 @@ browser use: https://github.com/vim/vim/issues/1234 -------------------- Known bugs and current work ----------------------- Vim9 - Make everything work: -- Implement "export {one, two three}". +- Does this work now: Implement using imported items at script level from + "import * as X" in +- import of item that isn't exported: error should mention missing "export"? +- no error for using :import in legacy script? - Disallow :open ? +- Check: what if 'cpo' is intentionally changed in Vim9 script, does it get + restored at the end? - ISN_CHECKTYPE could use check_argtype() - Using a script variable inside a :def function doesn't work if the variable is inside a block, see Test_nested_function(). Should it work? - give error for variable name: var p = function('NoSuchFunc') +- When indexing a string, should we include composing characters? #6563 + string[0] - first character including its composing characters. + string[0 : 0] - same + If you don't want that use strcharpart(). + Also, add optional arg to strcharpart() to include composing chars, to + make it consistent with strchars(). + Add strcharlen(), like strchars() but like skipcc is always set - Make closures work better: - Create closure in a loop. Need to make a list of them. - If a :def function is called with a function reference, compile it to get @@ -74,13 +86,11 @@ Vim9 - Make everything work: - make 0 == 'string' fail on the script level, like inside :def. - Check that when using a user function name without prefix, it does not find a global function. Prefixing g: is required. -- Need the equivalent of get_lval() and set_var_lval() to implement assignment - to nested list and dict members. - - Assignment to dict doesn't work: - var ret: dict = #{} - ret[i] = string(i) - - Appending to dict item doesn't work: - var d[i] ..= value +- Appending to dict item doesn't work in a :def function: + var d: dict = {a: 'x'} + d['a'] ..= 'y' + d.a ..= 'y' + Test to be extended: Test_assign_dict_with_op() - Using ".." at script level doesn't convert arguments to a string. - Compile replacement of :s command: s/pat/\=expr/ - Compile redir to local variable: var_redir_start(). @@ -89,8 +99,6 @@ Vim9 - Make everything work: islocked() - When evaluating constants for script variables, some functions could work: has('asdf'), len('string') -- Implement "as Name" in "import Item as Name from ..." -- Implement using imported items at script level from "import * as X" in eval_variable(). Should pass the ".xxx" that follows and return that. - Make "++nr" work. "++g:count" doesn't work, thinks it is a range. - Reload: How to make sure type of script function hasn't changed? @@ -102,6 +110,11 @@ Vim9 - Make everything work: - give an error for "echo Func()" if Func() does not return anything. - Using "windo echo expr" does not accept a line break inside "expr" (in a :def function and at script level in a not executed block). #7681 +- "assert_fails()" cannot access local variables. Perhaps add this: + assertfails + ... cmd ... + endassertfails /E99:.*cmd/ + Similar to try/catch/endtry but without the boilerplate. Once Vim9 is stable: - Change the help to prefer Vim9 syntax where appropriate @@ -184,6 +197,7 @@ Text properties: - Popup attached to text property stays visible when text is deleted with "cc". (#7737) "C" works OK. "dd" also files in a buffer with a single line. +- Auto-indenting may cause highlighting to shift. (#7719) - "cc" does not call inserted_bytes(). (Axel Forsman, #5763) - Combining text property with 'cursorline' does not always work (Billie Cleek, #5533) @@ -263,7 +277,9 @@ Terminal emulator window: - When 'encoding' is not utf-8, or the job is using another encoding, setup conversions. -Valgrind reports memory leaks in test_options +Include patch #6290: recognize shell directory change. + +Valgrind reports memory leaks in test_options. test_arglist func Test_all_not_allowed_from_cmdwin() hangs on MS-Windows. @@ -277,6 +293,8 @@ Was originally written by Felipe Morales. Adding "10" to 'spellsuggest' causes spell suggestions to become very slow. (#4087) Did patch 8.2.2379 help? +Also, z= in German on a long word can take a very long time, but CTRL-C to +interrupt does not work. Where to add ui_breakcheck()? Remove SPACE_IN_FILENAME ? It is only used for completion. @@ -288,6 +306,8 @@ with 'termguicolors'. #1740 Patch for blockwise paste reporting changes: #6660. +Patch to make fillchars global-local. (#5206) + Missing filetype test for bashrc, PKGBUILD, etc. Add an option to not fetch terminal codes in xterm, to avoid flicker when t_Co @@ -306,6 +326,10 @@ Try setting a color then request the current color, like using t_u7. Regexp to search for duplicate lines does not work correctly: /\(^.*\n\)\1 (Chris Morgan, #6239) +MS-Windows: when writing undo file the infostreams are copied in +mch_copy_file_attribute(), that seems unnecessary. (#7925) +Add a flag to only copy attributes? + Changing a capturing group to non-capturing changes the result: #7607 :echo matchstr('aaa bbb', '\(.\{-1,}\>\)\|.*') aaa @@ -382,6 +406,10 @@ manager. Problem with Motif? Patch to add :argdedupe. (Nir Lichtman, #6235) +When editing a file with ":edit" the output of :swapname is relative, while +editing it with "vim file" it is absolute. (#355) +Which one should it be? + :map output does not clear the reset of the command line. (#5623, also see #5962) @@ -1156,9 +1184,6 @@ timer expires. Rule to use "^" for statusline does not work if a space is defined with highlighting for both stl and stlnc. Patch by Ken Hamada (itchyny, 2016 Dec 11) -8 "stl" and "stlnc" in 'fillchars' don't work for multibyte characters. - Patch by Christian Wellenbrock, 2013 Jul 5. - Using CTRL-G_U in InsertCharPre causes trouble for redo. (Israel Chauca Fuentes, 2017 Feb 12, #1470) diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 8113bef8e5..0d17298fb0 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -611,7 +611,8 @@ String manipulation: *string-functions* stridx() first index of a short string in a long string strridx() last index of a short string in a long string strlen() length of a string in bytes - strchars() length of a string in characters + strcharlen() length of a string in characters + strchars() number of characters in a string strwidth() size of string when displayed strdisplaywidth() size of string when displayed, deals with tabs setcellwidths() set character cell width overrides diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 2d6ec59c44..794b86ca28 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -1,4 +1,4 @@ -*various.txt* For Vim version 8.2. Last change: 2021 Jan 26 +*various.txt* For Vim version 8.2. Last change: 2021 Mar 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -575,7 +575,7 @@ N *+X11* Unix only: can restore window title |X11| it in / any non-ID character (see |'isident'|) can be used, so long as it does not appear in {pat}. Without the enclosing character the pattern cannot include the - bar character. + bar character. 'ignorecase' is not used. The pattern is matched against the relevant part of the output, not necessarily the whole line. Only some diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index 016089a87c..4e8016c9db 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 8.2. Last change: 2021 Feb 23 +*vim9.txt* For Vim version 8.2. Last change: 2021 Mar 03 VIM REFERENCE MANUAL by Bram Moolenaar @@ -96,8 +96,8 @@ script and `:def` functions; details are below: def CallMe(count: number, message: string): bool - Call functions without `:call`: > writefile(['done'], 'file.txt') -- You cannot use `:xit`, `:t`, `:k`, `:append`, `:change`, `:insert` or - curly-braces names. +- You cannot use `:xit`, `:t`, `:k`, `:append`, `:change`, `:insert`, `:open` + or curly-braces names. - A range before a command must be prefixed with a colon: > :%s/this/that - Unless mentioned specifically, the highest |scriptversion| is used. @@ -341,7 +341,8 @@ Functions can be called without `:call`: > Using `:call` is still possible, but this is discouraged. A method call without `eval` is possible, so long as the start is an -identifier or can't be an Ex command. Examples: > +identifier or can't be an Ex command. For a function either "(" or "->" must +be following, without a line break. Examples: > myList->add(123) g:myList->add(123) [1, 2, 3]->Process() @@ -696,8 +697,9 @@ for v:null. When converting a boolean to a string "false" and "true" are used, not "v:false" and "v:true" like in legacy script. "v:none" is not changed, it is only used in JSON and has no equivalent in other languages. -Indexing a string with [idx] or [idx : idx] uses character indexes instead of -byte indexes. Example: > +Indexing a string with [idx] or taking a slice with [idx : idx] uses character +indexes instead of byte indexes. Composing characters are included. +Example: > echo 'bár'[1] In legacy script this results in the character 0xc3 (an illegal byte), in Vim9 script this results in the string 'á'. @@ -800,6 +802,8 @@ Patterns are used like 'magic' is set, unless explicitly overruled. The 'edcompatible' option value is not used. The 'gdefault' option value is not used. +You may also find this wiki useful. It was written by an early adoptor of +Vim9 script: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md ============================================================================== @@ -843,6 +847,8 @@ THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE :enddef End of a function defined with `:def`. It should be on a line by its own. +You may also find this wiki useful. It was written by an early adoptor of +Vim9 script: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md If the script the function is defined in is Vim9 script, then script-local variables can be accessed without the "s:" prefix. They must be defined @@ -1076,7 +1082,9 @@ A side effect of `:vim9script` is that the 'cpoptions' option is set to the Vim default value, like with: > :set cpo&vim One of the effects is that |line-continuation| is always enabled. -The original value of 'cpoptions' is restored at the end of the script. +The original value of 'cpoptions' is restored at the end of the script, while +flags added or removed in the script are also added to or removed from the +original value to get the same effect. The order of flags may change. *vim9-mix* There is one way to use both legacy and Vim9 syntax in one script file: > @@ -1111,7 +1119,7 @@ Exporting an item can be written as: > export class MyClass ... As this suggests, only constants, variables, `:def` functions and classes can -be exported. {classes are not implemented yet} +be exported. {not implemented yet: export class} *E1042* `:export` can only be used in Vim9 script, at the script level. diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 259e5eeb3c..1a18a651e0 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: Bram Moolenaar -" Last Change: 2021 Jan 21 +" Last Change: 2021 Mar 12 " Listen very carefully, I will say this only once if exists("did_load_filetypes") @@ -1334,6 +1334,9 @@ au BufNewFile,BufRead *.pml setf promela au BufNewFile,BufRead *.proto setf proto au BufNewFile,BufRead *.pbtxt setf pbtxt +" Poke +au BufNewFile,BufRead *.pk setf poke + " Protocols au BufNewFile,BufRead */etc/protocols setf protocols diff --git a/runtime/ftplugin/poke.vim b/runtime/ftplugin/poke.vim new file mode 100644 index 0000000000..2be86695c8 --- /dev/null +++ b/runtime/ftplugin/poke.vim @@ -0,0 +1,32 @@ +" Vim filetype plugin file +" Language: GNU Poke +" Maintainer: Doug Kearns +" Last Change: 2021 March 11 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:cpo_save = &cpo +set cpo&vim + +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// +setlocal commentstring=//\ %s +setlocal formatoptions-=t formatoptions+=croql + +setlocal include=load +setlocal suffixesadd=.pk + +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Poke Files (*.pk)\t*.pk\n" . + \ "All Files (*.*)\t*.*\n" +endif + +let b:undo_ftplugin = "setl fo< com< cms< inc< sua<" . + \ " | unlet! b:browsefilter" + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: nowrap sw=2 sts=2 ts=8 diff --git a/runtime/syntax/html.vim b/runtime/syntax/html.vim index 36d3c25f3a..8ccb5574e7 100644 --- a/runtime/syntax/html.vim +++ b/runtime/syntax/html.vim @@ -3,8 +3,9 @@ " Previous Maintainer: Jorge Maldonado Ventura " Previous Maintainer: Claudio Fleiner " Repository: https://notabug.org/jorgesumle/vim-html-syntax -" Last Change: 2021 Feb 25 +" Last Change: 2021 Mar 02 " Included patch #7900 to fix comments +" Included patch #7916 to fix a few more things " " Please check :help html.vim for some comments and a description of the options @@ -79,26 +80,16 @@ syn keyword htmlArg contained usemap ismap valign value vlink vspace width wrap syn match htmlArg contained "\<\(http-equiv\|href\|title\)="me=e-1 " aria attributes -syn match htmlArg contained "\<\(aria-activedescendant\|aria-atomic\)\>" -syn match htmlArg contained "\<\(aria-autocomplete\|aria-busy\|aria-checked\)\>" -syn match htmlArg contained "\<\(aria-colcount\|aria-colindex\|aria-colspan\)\>" -syn match htmlArg contained "\<\(aria-controls\|aria-current\)\>" -syn match htmlArg contained "\<\(aria-describedby\|aria-details\)\>" -syn match htmlArg contained "\<\(aria-disabled\|aria-dropeffect\)\>" -syn match htmlArg contained "\<\(aria-errormessage\|aria-expanded\)\>" -syn match htmlArg contained "\<\(aria-flowto\|aria-grabbed\|aria-haspopup\)\>" -syn match htmlArg contained "\<\(aria-hidden\|aria-invalid\)\>" -syn match htmlArg contained "\<\(aria-keyshortcuts\|aria-label\)\>" -syn match htmlArg contained "\<\(aria-labelledby\|aria-level\|aria-live\)\>" -syn match htmlArg contained "\<\(aria-modal\|aria-multiline\)\>" -syn match htmlArg contained "\<\(aria-multiselectable\|aria-orientation\)\>" -syn match htmlArg contained "\<\(aria-owns\|aria-placeholder\|aria-posinset\)\>" -syn match htmlArg contained "\<\(aria-pressed\|aria-readonly\|aria-relevant\)\>" -syn match htmlArg contained "\<\(aria-required\|aria-roledescription\)\>" -syn match htmlArg contained "\<\(aria-rowcount\|aria-rowindex\|aria-rowspan\)\>" -syn match htmlArg contained "\<\(aria-selected\|aria-setsize\|aria-sort\)\>" -syn match htmlArg contained "\<\(aria-valuemax\|aria-valuemin\)\>" -syn match htmlArg contained "\<\(aria-valuenow\|aria-valuetext\)\>" +exe 'syn match htmlArg contained "\"' syn keyword htmlArg contained role " Netscape extensions @@ -139,25 +130,19 @@ syn match htmlSpecialChar "&#\=[0-9A-Za-z]\{1,8};" " Comments (the real ones or the old netscape ones) if exists("html_wrong_comments") - syn region htmlComment start=+ and are parser errors; browser treats as comments - syn match htmlComment " closes comment - " Idem 8.2.4.52: closing comment by dash-dash-bang (--!>) is error ignored by parser(!); closes comment - syn region htmlCommentPart contained start=+--+ end=+--!\?>+me=e-1 contains=htmlCommentNested,@htmlPreProc,@Spell - " Idem 8.2.4.49: opening nested comment is all right - syn match htmlCommentNested contained ""me=e-3 - syn match htmlCommentNested contained " + " Idem 8.2.4.43,44: Except and are parser errors + " Idem 8.2.4.52: dash-dash-bang (--!>) is error ignored by parser, also closes comment + syn region htmlComment matchgroup=htmlComment start=+ is all right + syn match htmlCommentNested contained "\@!" + syn match htmlCommentError contained "[^>+ +syn region htmlComment start=++ keepend " server-parsed commands syn region htmlPreProc start=++ contains=htmlPreStmt,htmlPreError,htmlPreAttr @@ -278,7 +263,7 @@ hi def link htmlEndTag Identifier hi def link htmlArg Type hi def link htmlTagName htmlStatement hi def link htmlSpecialTagName Exception -hi def link htmlValue String +hi def link htmlValue String hi def link htmlSpecialChar Special if !exists("html_no_rendering") @@ -322,14 +307,10 @@ hi def link htmlPreProc PreProc hi def link htmlPreAttr String hi def link htmlPreProcAttrName PreProc hi def link htmlPreProcAttrError Error -hi def link htmlSpecial Special -hi def link htmlSpecialChar Special hi def link htmlString String hi def link htmlStatement Statement hi def link htmlComment Comment -hi def link htmlCommentPart Comment -hi def link htmlValue String -hi def link htmlCommentNested htmlCommentError +hi def link htmlCommentNested htmlError hi def link htmlCommentError htmlError hi def link htmlTagError htmlError hi def link htmlEvent javaScript diff --git a/runtime/syntax/poke.vim b/runtime/syntax/poke.vim new file mode 100644 index 0000000000..4a07a77d6c --- /dev/null +++ b/runtime/syntax/poke.vim @@ -0,0 +1,151 @@ +" Copyright (C) 2021 Matthew T. Ihlenfield. +" +" This program is free software: you can redistribute it and/or modify +" it under the terms of the GNU General Public License as published by +" the Free Software Foundation, either version 3 of the License, or +" (at your option) any later version. +" +" This program is distributed in the hope that it will be useful, +" but WITHOUT ANY WARRANTY; without even the implied warranty of +" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +" GNU General Public License for more details. +" +" You should have received a copy of the GNU General Public License +" along with this program. If not, see . +" +" Vim syntax file +" Language: Poke +" Maintainer: Matt Ihlenfield +" Filenames: *.pk +" Latest Revision: 10 March 2021 + +if exists('b:current_syntax') + finish +endif + +" Poke statement +syn keyword pokeStatement assert break continue return +syn keyword pokeStatement type unit fun method nextgroup=pokeFunction skipwhite +syn keyword pokeStatement var nextgroup=pokeVar skipWhite + +" Identifiers +syn match pokeVar '\h\w*' display contained + +" User defined functions +syn match pokeFunction '\h\w*' display contained + +" Poke operators +syn keyword pokeOperator in sizeof as isa unmap + +" Conditionals +syn keyword pokeConditional if else where + +" Structures, unions, etc... +syn keyword pokeStructure struct union pinned + +" Loops +syn keyword pokeRepeat while for + +" Imports +syn keyword pokeLoad load + +" Exceptions +syn keyword pokeException try catch until raise + +" Exception types +syn keyword pokeExceptionType Exception E_generic E_out_of_bounds +syn keyword pokeExceptionType E_eof E_elem E_constraint +syn keyword pokeExceptionType E_conv E_map_bounds E_map +syn keyword pokeExceptionType E_div_by_zero E_no_ios E_no_return +syn keyword pokeExceptionType E_io E_io_flags E_assert E_overflow + +" Exception codes +syn keyword pokeExceptionCode EC_generic EC_out_of_bounds +syn keyword pokeExceptionCode EC_eof EC_elem EC_constraint +syn keyword pokeExceptionCode EC_conv EC_map_bounds EC_map +syn keyword pokeExceptionCode EC_div_by_zero EC_no_ios EC_no_return +syn keyword pokeExceptionCode EC_io EC_io_flags EC_assert EC_overflow + +" Poke builtin types +syn keyword pokeBuiltinType string void int uint bit nibble +syn keyword pokeBuiltinType byte char ushort short ulong long +syn keyword pokeBuiltinType uint8 uint16 uint32 uint64 +syn keyword pokeBuiltinType off64 uoff64 offset +syn keyword pokeBuiltinType Comparator POSIX_Time32 POSIX_Time64 +syn keyword pokeBuiltinType big little any + +" Poke constants +syn keyword pokeConstant ENDIAN_LITTLE ENDIAN_BIG +syn keyword pokeConstant IOS_F_READ IOS_F_WRITE IOS_F_TRUNCATE IOS_F_CREATE +syn keyword pokeConstant IOS_M_RDONLY IOS_M_WRONLY IOS_M_RDWR +syn keyword pokeConstant load_path NULL OFFSET + +" Poke std lib +syn keyword pokeBuiltinFunction print printf catos stoca atoi ltos reverse +syn keyword pokeBuiltinFunction ltrim rtrim strchr qsort crc32 alignto +syn keyword pokeBuiltinFunction open close flush get_ios set_ios iosize +syn keyword pokeBuiltinFunction rand get_endian set_endian strace exit +syn keyword pokeBuiltinFunction getenv + +" Formats + +" Special chars +syn match pokeSpecial "\\\([nt\\]\|\o\{1,3}\)" display contained + +" Chars +syn match pokeChar "'[^']*'" contains=pokeSpecial + +" Attributes +syn match pokeAttribute "\h\w*'\h\w" + +" Strings +syn region pokeString skip=+\\\\\|\\"+ start=+"+ end=+"+ contains=pokeSpecial + +" Integer literals +syn match pokeInteger "\<\d\+_*\d*\([LlHhBbNn]\=[Uu]\=\|[Uu]\=[LlHhBbNn]\=\)\>" +syn match pokeInteger "\<0[Xx]\x\+_*\x*\([LlHhBbNn]\=[Uu]\=\|[Uu]\=[LlHhBbNn]\=\)\>" +syn match pokeInteger "\<0[Oo]\o\+_*\o*\([LlHhBbNn]\=[Uu]\=\|[Uu]\=[LlHhBbNn]\=\)\>" +syn match pokeInteger "\<0[Bb][01]\+_*[01]*\([LlHhBbNn]\=[Uu]\=\|[Uu]\=[LlHhBbNn]\=\)\>" + +" Units +syn keyword pokeBuiltinUnit b M B +syn keyword pokeBuiltinUnit Kb KB Mb MB Gb GB +syn keyword pokeBuiltinUnit Kib KiB Mib MiB Gib GiB + +" Offsets +syn match pokeOffset "#\h\w*" contains=pokeBuiltinUnit + +" Comments +syn keyword pokeCommentTodo TODO FIXME XXX TBD contained +syn match pokeLineComment "\/\/.*" contains=pokeCommentTodo,@Spell extend +syn region pokeComment start="/\*" end="\*/" contains=pokeCommentTodo,@Spell fold extend + +" Allow folding of blocks +syn region pokeBlock start="{" end="}" transparent fold + +" Highlight groups +hi def link pokeBuiltinFunction Function +hi def link pokeBuiltinType Type +hi def link pokeBuiltinUnit Keyword +hi def link pokeChar Character +hi def link pokeComment Comment +hi def link pokeCommentTodo Todo +hi def link pokeConditional Conditional +hi def link pokeConstant Constant +hi def link pokeException Exception +hi def link pokeExceptionCode Constant +hi def link pokeExceptionType Type +hi def link pokeFunction Function +hi def link pokeInteger Number +hi def link pokeLineComment Comment +hi def link pokeLoad Include +hi def link pokeOffset StorageClass +hi def link pokeOperator Operator +hi def link pokeSpecial SpecialChar +hi def link pokeStatement Statement +hi def link pokeString String +hi def link pokeStructure Structure +hi def link pokeRepeat Repeat +hi def link pokeVar Identifier + +let b:current_syntax = 'poke' diff --git a/src/INSTALL b/src/INSTALL index 1ed4887c77..64958aa13f 100644 --- a/src/INSTALL +++ b/src/INSTALL @@ -60,8 +60,8 @@ To built Vim on Ubuntu from scratch on a clean system using git: % sudo apt install libxt-dev % make reconfig - Add GUI support (ignore compiler warnings): - % sudo apt install libgtk2.0-dev + Add GUI support: + % sudo apt install libgtk-3-dev % make reconfig Add Python 3 support: @@ -134,8 +134,12 @@ These configure arguments can be used to select which GUI to use: --disable-motif-check --disable-athena-check +This configure argument can be used to disable the GUI, even when the necessary +files are found: +--disable-gui + --enable-gui defaults to "auto", so it will automatically look for a GUI (in -the order of GTK, Motif, then Athena). If one is found, then is uses it and +the order of GTK, Motif, then Athena). If one is found, then it is used and does not proceed to check any of the remaining ones. Otherwise, it moves on to the next one. diff --git a/src/auto/configure b/src/auto/configure index 3f7cf417d8..21758d4f92 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -14148,6 +14148,35 @@ $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysinfo.uptime" >&5 +$as_echo_n "checking for sysinfo.uptime... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +int +main () +{ + struct sysinfo sinfo; + long ut; + + (void)sysinfo(&sinfo); + ut = sinfo.uptime; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; }; $as_echo "#define HAVE_SYSINFO_UPTIME 1" >>confdefs.h + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysconf" >&5 $as_echo_n "checking for sysconf... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext diff --git a/src/autocmd.c b/src/autocmd.c index 8da507aee8..d212bcb16e 100644 --- a/src/autocmd.c +++ b/src/autocmd.c @@ -1337,7 +1337,7 @@ do_doautocmd( void ex_doautoall(exarg_T *eap) { - int retval; + int retval = OK; aco_save_T aco; buf_T *buf; bufref_T bufref; @@ -1354,7 +1354,8 @@ ex_doautoall(exarg_T *eap) */ FOR_ALL_BUFFERS(buf) { - if (buf->b_ml.ml_mfp != NULL) + // Only do loaded buffers and skip the current buffer, it's done last. + if (buf->b_ml.ml_mfp != NULL && buf != curbuf) { // find a window for this buffer and save some values aucmd_prepbuf(&aco, buf); @@ -1364,22 +1365,31 @@ ex_doautoall(exarg_T *eap) retval = do_doautocmd(arg, FALSE, &did_aucmd); if (call_do_modelines && did_aucmd) - { // Execute the modeline settings, but don't set window-local // options if we are using the current window for another // buffer. do_modelines(curwin == aucmd_win ? OPT_NOWIN : 0); - } // restore the current window aucmd_restbuf(&aco); // stop if there is some error or buffer was deleted if (retval == FAIL || !bufref_valid(&bufref)) + { + retval = FAIL; break; + } } } + // Execute autocommands for the current buffer last. + if (retval == OK) + { + do_doautocmd(arg, FALSE, &did_aucmd); + if (call_do_modelines && did_aucmd) + do_modelines(0); + } + check_cursor(); // just in case lines got deleted } @@ -2167,12 +2177,14 @@ apply_autocmds_group( while (au_pending_free_buf != NULL) { buf_T *b = au_pending_free_buf->b_next; + vim_free(au_pending_free_buf); au_pending_free_buf = b; } while (au_pending_free_win != NULL) { win_T *w = au_pending_free_win->w_next; + vim_free(au_pending_free_win); au_pending_free_win = w; } diff --git a/src/config.h.in b/src/config.h.in index 5d01e2c4f7..fbf4b2449c 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -213,6 +213,7 @@ #undef HAVE_SYSCTL #undef HAVE_SYSINFO #undef HAVE_SYSINFO_MEM_UNIT +#undef HAVE_SYSINFO_UPTIME #undef HAVE_TGETENT #undef HAVE_TOWLOWER #undef HAVE_TOWUPPER diff --git a/src/configure.ac b/src/configure.ac index 0e1466b5a4..226faec21a 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -4280,6 +4280,20 @@ AC_TRY_COMPILE( AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSINFO_MEM_UNIT), AC_MSG_RESULT(no)) +dnl struct sysinfo may have the uptime field or not +AC_MSG_CHECKING(for sysinfo.uptime) +AC_TRY_COMPILE( +[#include +#include ], +[ struct sysinfo sinfo; + long ut; + + (void)sysinfo(&sinfo); + ut = sinfo.uptime; + ], + AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SYSINFO_UPTIME), + AC_MSG_RESULT(no)) + dnl sysconf() may exist but not support what we want to use AC_MSG_CHECKING(for sysconf) AC_TRY_COMPILE( diff --git a/src/drawline.c b/src/drawline.c index 76a9786a2a..3d421bd0f4 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1034,7 +1034,7 @@ win_line( p_extra_free = alloc(MAX_MCO * fdc + 1); if (p_extra_free != NULL) { - n_extra = fill_foldcolumn(p_extra_free, wp, + n_extra = (int)fill_foldcolumn(p_extra_free, wp, FALSE, lnum); p_extra_free[n_extra] = NUL; p_extra = p_extra_free; diff --git a/src/errors.h b/src/errors.h index db958e0541..c1dc547fbe 100644 --- a/src/errors.h +++ b/src/errors.h @@ -373,3 +373,5 @@ EXTERN char e_argument_name_shadows_existing_variable_str[] INIT(= N_("E1167: Argument name shadows existing variable: %s")); EXTERN char e_argument_already_declared_in_script_str[] INIT(= N_("E1168: Argument already declared in the script: %s")); +EXTERN char e_import_as_name_not_supported_here[] + INIT(= N_("E1169: 'import * as {name}' not supported here")); diff --git a/src/eval.c b/src/eval.c index 1ad6339659..8e964de700 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1370,7 +1370,7 @@ set_var_lval( // handle +=, -=, *=, /=, %= and .= di = NULL; if (eval_variable(lp->ll_name, (int)STRLEN(lp->ll_name), - &tv, &di, TRUE, FALSE) == OK) + &tv, &di, EVAL_VAR_VERBOSE) == OK) { if ((di == NULL || (!var_check_ro(di->di_flags, lp->ll_name, FALSE) @@ -3500,7 +3500,8 @@ eval7( ret = OK; } else - ret = eval_variable(s, len, rettv, NULL, TRUE, FALSE); + ret = eval_variable(s, len, rettv, NULL, + EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT); } else { @@ -5760,6 +5761,63 @@ handle_subscript( check_white = FALSE; } + if (rettv->v_type == VAR_ANY) + { + char_u *exp_name; + int cc; + int idx; + ufunc_T *ufunc; + type_T *type; + + // Found script from "import * as {name}", script item name must + // follow. + if (**arg != '.') + { + if (verbose) + semsg(_(e_expected_str_but_got_str), "'.'", *arg); + ret = FAIL; + break; + } + ++*arg; + if (IS_WHITE_OR_NUL(**arg)) + { + if (verbose) + emsg(_(e_no_white_space_allowed_after_dot)); + ret = FAIL; + break; + } + + // isolate the name + exp_name = *arg; + while (eval_isnamec(**arg)) + ++*arg; + cc = **arg; + **arg = NUL; + + idx = find_exported(rettv->vval.v_number, exp_name, &ufunc, &type, + evalarg->eval_cctx, verbose); + **arg = cc; + *arg = skipwhite(*arg); + + if (idx < 0 && ufunc == NULL) + { + ret = FAIL; + break; + } + if (idx >= 0) + { + scriptitem_T *si = SCRIPT_ITEM(rettv->vval.v_number); + svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; + + copy_tv(sv->sv_tv, rettv); + } + else + { + rettv->v_type = VAR_FUNC; + rettv->vval.v_string = vim_strsave(ufunc->uf_name); + } + } + if ((**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC || rettv->v_type == VAR_PARTIAL)) && (!check_white || !VIM_ISWHITE(*(*arg - 1)))) diff --git a/src/evalfunc.c b/src/evalfunc.c index 1a83f0f49f..f76a7f7ee6 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -223,6 +223,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv); #endif static void f_str2list(typval_T *argvars, typval_T *rettv); static void f_str2nr(typval_T *argvars, typval_T *rettv); +static void f_strcharlen(typval_T *argvars, typval_T *rettv); static void f_strchars(typval_T *argvars, typval_T *rettv); static void f_strgetchar(typval_T *argvars, typval_T *rettv); static void f_stridx(typval_T *argvars, typval_T *rettv); @@ -1021,7 +1022,7 @@ static funcentry_T global_functions[] = {"getcwd", 0, 2, FEARG_1, NULL, ret_string, f_getcwd}, {"getenv", 1, 1, FEARG_1, NULL, - ret_string, f_getenv}, + ret_any, f_getenv}, {"getfontname", 0, 1, 0, NULL, ret_string, f_getfontname}, {"getfperm", 1, 1, FEARG_1, NULL, @@ -1572,7 +1573,9 @@ static funcentry_T global_functions[] = ret_list_number, f_str2list}, {"str2nr", 1, 3, FEARG_1, arg3_string_nr_bool, ret_number, f_str2nr}, - {"strcharpart", 2, 3, FEARG_1, NULL, + {"strcharlen", 1, 1, FEARG_1, NULL, + ret_number, f_strcharlen}, + {"strcharpart", 2, 4, FEARG_1, NULL, ret_string, f_strcharpart}, {"strchars", 1, 2, FEARG_1, NULL, ret_number, f_strchars}, @@ -9271,31 +9274,45 @@ f_strlen(typval_T *argvars, typval_T *rettv) tv_get_string(&argvars[0]))); } + static void +strchar_common(typval_T *argvars, typval_T *rettv, int skipcc) +{ + char_u *s = tv_get_string(&argvars[0]); + varnumber_T len = 0; + int (*func_mb_ptr2char_adv)(char_u **pp); + + func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; + while (*s != NUL) + { + func_mb_ptr2char_adv(&s); + ++len; + } + rettv->vval.v_number = len; +} + +/* + * "strcharlen()" function + */ + static void +f_strcharlen(typval_T *argvars, typval_T *rettv) +{ + strchar_common(argvars, rettv, TRUE); +} + /* * "strchars()" function */ static void f_strchars(typval_T *argvars, typval_T *rettv) { - char_u *s = tv_get_string(&argvars[0]); varnumber_T skipcc = FALSE; - varnumber_T len = 0; - int (*func_mb_ptr2char_adv)(char_u **pp); if (argvars[1].v_type != VAR_UNKNOWN) skipcc = tv_get_bool(&argvars[1]); if (skipcc < 0 || skipcc > 1) semsg(_(e_using_number_as_bool_nr), skipcc); else - { - func_mb_ptr2char_adv = skipcc ? mb_ptr2char_adv : mb_cptr2char_adv; - while (*s != NUL) - { - func_mb_ptr2char_adv(&s); - ++len; - } - rettv->vval.v_number = len; - } + strchar_common(argvars, rettv, skipcc); } /* @@ -9334,6 +9351,7 @@ f_strcharpart(typval_T *argvars, typval_T *rettv) int nchar; int nbyte = 0; int charlen; + int skipcc = FALSE; int len = 0; int slen; int error = FALSE; @@ -9344,10 +9362,24 @@ f_strcharpart(typval_T *argvars, typval_T *rettv) nchar = (int)tv_get_number_chk(&argvars[1], &error); if (!error) { + if (argvars[2].v_type != VAR_UNKNOWN + && argvars[3].v_type != VAR_UNKNOWN) + { + skipcc = tv_get_bool(&argvars[3]); + if (skipcc < 0 || skipcc > 1) + { + semsg(_(e_using_number_as_bool_nr), skipcc); + return; + } + } + if (nchar > 0) while (nchar > 0 && nbyte < slen) { - nbyte += MB_CPTR2LEN(p + nbyte); + if (skipcc) + nbyte += mb_ptr2len(p + nbyte); + else + nbyte += MB_CPTR2LEN(p + nbyte); --nchar; } else @@ -9362,7 +9394,12 @@ f_strcharpart(typval_T *argvars, typval_T *rettv) if (off < 0) len += 1; else - len += MB_CPTR2LEN(p + off); + { + if (skipcc) + len += mb_ptr2len(p + off); + else + len += MB_CPTR2LEN(p + off); + } --charlen; } } diff --git a/src/evalvars.c b/src/evalvars.c index 47af103707..3d425d8809 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -321,7 +321,8 @@ garbage_collect_scriptvars(int copyID) { svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; - abort = abort || set_ref_in_item(sv->sv_tv, copyID, NULL, NULL); + if (sv->sv_name != NULL) + abort = abort || set_ref_in_item(sv->sv_tv, copyID, NULL, NULL); } } @@ -1220,7 +1221,8 @@ list_arg_vars(exarg_T *eap, char_u *arg, int *first) arg = skipwhite(arg); if (tofree != NULL) name = tofree; - if (eval_variable(name, len, &tv, NULL, TRUE, FALSE) == FAIL) + if (eval_variable(name, len, &tv, NULL, + EVAL_VAR_VERBOSE) == FAIL) error = TRUE; else { @@ -2540,6 +2542,8 @@ set_cmdarg(exarg_T *eap, char_u *oldarg) /* * Get the value of internal variable "name". + * If "flags" has EVAL_VAR_IMPORT may return a VAR_ANY with v_number set to the + * imported script ID. * Return OK or FAIL. If OK is returned "rettv" must be cleared. */ int @@ -2548,12 +2552,11 @@ eval_variable( int len, // length of "name" typval_T *rettv, // NULL when only checking existence dictitem_T **dip, // non-NULL when typval's dict item is needed - int verbose, // may give error message - int no_autoload) // do not use script autoloading + int flags) // EVAL_VAR_ flags { int ret = OK; typval_T *tv = NULL; - int foundFunc = FALSE; + int found = FALSE; dictitem_T *v; int cc; @@ -2562,7 +2565,7 @@ eval_variable( name[len] = NUL; // Check for user-defined variables. - v = find_var(name, NULL, no_autoload); + v = find_var(name, NULL, flags & EVAL_VAR_NOAUTOLOAD); if (v != NULL) { tv = &v->di_tv; @@ -2582,7 +2585,7 @@ eval_variable( { if (import->imp_funcname != NULL) { - foundFunc = TRUE; + found = TRUE; if (rettv != NULL) { rettv->v_type = VAR_FUNC; @@ -2591,8 +2594,21 @@ eval_variable( } else if (import->imp_flags & IMP_FLAGS_STAR) { - emsg("Sorry, 'import * as X' not implemented yet"); - ret = FAIL; + if ((flags & EVAL_VAR_IMPORT) == 0) + { + if (flags & EVAL_VAR_VERBOSE) + emsg(_(e_import_as_name_not_supported_here)); + ret = FAIL; + } + else + { + if (rettv != NULL) + { + rettv->v_type = VAR_ANY; + rettv->vval.v_number = import->imp_sid; + } + found = TRUE; + } } else { @@ -2608,7 +2624,7 @@ eval_variable( if (ufunc != NULL) { - foundFunc = TRUE; + found = TRUE; if (rettv != NULL) { rettv->v_type = VAR_FUNC; @@ -2618,11 +2634,11 @@ eval_variable( } } - if (!foundFunc) + if (!found) { if (tv == NULL) { - if (rettv != NULL && verbose) + if (rettv != NULL && (flags & EVAL_VAR_VERBOSE)) semsg(_(e_undefined_variable_str), name); ret = FAIL; } @@ -2791,12 +2807,15 @@ get_script_local_ht(void) /* * Look for "name[len]" in script-local variables and functions. + * When "cmd" is TRUE it must look like a command, a function must be followed + * by "(" or "->". * Return OK when found, FAIL when not found. */ int lookup_scriptitem( char_u *name, size_t len, + int cmd, cctx_T *dummy UNUSED) { hashtab_T *ht = get_script_local_ht(); @@ -2831,19 +2850,26 @@ lookup_scriptitem( if (p != buffer) vim_free(p); + // Find a function, so that a following "->" works. + // When used as a command require "(" or "->" to follow, "Cmd" is a user + // command while "Cmd()" is a function call. if (res != OK) { - // Find a function, so that a following "->" works. Skip "g:" before a - // function name. - // Do not check for an internal function, since it might also be a - // valid command, such as ":split" versuse "split()". - if (name[0] == 'g' && name[1] == ':') + p = skipwhite(name + len); + + if (!cmd || name[len] == '(' || (p[0] == '-' && p[1] == '>')) { - is_global = TRUE; - fname = name + 2; + // Do not check for an internal function, since it might also be a + // valid command, such as ":split" versus "split()". + // Skip "g:" before a function name. + if (name[0] == 'g' && name[1] == ':') + { + is_global = TRUE; + fname = name + 2; + } + if (find_func(fname, is_global, NULL) != NULL) + res = OK; } - if (find_func(fname, is_global, NULL) != NULL) - res = OK; } return res; @@ -3274,7 +3300,7 @@ set_var_const( { // Item not found, check if a function already exists. if (is_script_local && (flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0 - && lookup_scriptitem(name, STRLEN(name), NULL) == OK) + && lookup_scriptitem(name, STRLEN(name), FALSE, NULL) == OK) { semsg(_(e_redefining_script_item_str), name); goto failed; @@ -3696,7 +3722,8 @@ var_exists(char_u *var) { if (tofree != NULL) name = tofree; - n = (eval_variable(name, len, &tv, NULL, FALSE, TRUE) == OK); + n = (eval_variable(name, len, &tv, NULL, + EVAL_VAR_NOAUTOLOAD + EVAL_VAR_IMPORT) == OK); if (n) { // handle d.key, l[idx], f(expr) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 3fdb7535b7..c241b03b9b 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -3317,7 +3317,7 @@ skip_option_env_lead(char_u *start) find_ex_command( exarg_T *eap, int *full UNUSED, - int (*lookup)(char_u *, size_t, cctx_T *) UNUSED, + int (*lookup)(char_u *, size_t, int cmd, cctx_T *) UNUSED, cctx_T *cctx UNUSED) { int len; @@ -3436,7 +3436,7 @@ find_ex_command( || *eap->cmd == '&' || *eap->cmd == '$' || *eap->cmd == '@' - || lookup(eap->cmd, p - eap->cmd, cctx) == OK) + || lookup(eap->cmd, p - eap->cmd, TRUE, cctx) == OK) { eap->cmdidx = CMD_var; return eap->cmd; @@ -3455,7 +3455,7 @@ find_ex_command( // If it is an ID it might be a variable with an operator on the next // line, if the variable exists it can't be an Ex command. if (p > eap->cmd && ends_excmd(*skipwhite(p)) - && (lookup(eap->cmd, p - eap->cmd, cctx) == OK + && (lookup(eap->cmd, p - eap->cmd, TRUE, cctx) == OK || (ASCII_ISALPHA(eap->cmd[0]) && eap->cmd[1] == ':'))) { eap->cmdidx = CMD_eval; @@ -6615,6 +6615,10 @@ ex_open(exarg_T *eap) regmatch_T regmatch; char_u *p; +#ifdef FEAT_EVAL + if (not_in_vim9(eap) == FAIL) + return; +#endif curwin->w_cursor.lnum = eap->line2; beginline(BL_SOL | BL_FIX); if (*eap->arg == '/') diff --git a/src/globals.h b/src/globals.h index e66f902ba4..48d0bde666 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1865,6 +1865,7 @@ EXTERN int nfa_fail_for_testing INIT(= FALSE); EXTERN int no_query_mouse_for_testing INIT(= FALSE); EXTERN int ui_delay_for_testing INIT(= 0); EXTERN int reset_term_props_on_termresponse INIT(= FALSE); +EXTERN long override_sysinfo_uptime INIT(= -1); EXTERN int in_free_unref_items INIT(= FALSE); #endif diff --git a/src/if_lua.c b/src/if_lua.c index e20298311d..abcd850b3d 100644 --- a/src/if_lua.c +++ b/src/if_lua.c @@ -568,6 +568,11 @@ luaV_pushtypval(lua_State *L, typval_T *tv) case VAR_FUNC: luaV_pushfuncref(L, tv->vval.v_string); break; + case VAR_PARTIAL: + // TODO: handle partial arguments + luaV_pushfuncref(L, partial_name(tv->vval.v_partial)); + break; + case VAR_BLOB: luaV_pushblob(L, tv->vval.v_blob); break; diff --git a/src/main.c b/src/main.c index 6cf138b789..a58be11deb 100644 --- a/src/main.c +++ b/src/main.c @@ -3627,8 +3627,11 @@ usage(void) #endif // FEAT_GUI_X11 #ifdef FEAT_GUI_GTK mch_msg(_("\nArguments recognised by gvim (GTK+ version):\n")); + main_msg(_("-background \tUse for the background (also: -bg)")); + main_msg(_("-foreground \tUse for normal text (also: -fg)")); main_msg(_("-font \t\tUse for normal text (also: -fn)")); main_msg(_("-geometry \tUse for initial geometry (also: -geom)")); + main_msg(_("-iconic\t\tStart Vim iconified")); main_msg(_("-reverse\t\tUse reverse video (also: -rv)")); main_msg(_("-display \tRun Vim on (also: --display)")); main_msg(_("--role \tSet a unique role to identify the main window")); diff --git a/src/memline.c b/src/memline.c index c5303bb837..4c4a1b24cb 100644 --- a/src/memline.c +++ b/src/memline.c @@ -1080,6 +1080,35 @@ add_b0_fenc( } } +#if defined(HAVE_SYS_SYSINFO_H) && defined(HAVE_SYSINFO_UPTIME) +# include +#endif + +/* + * Return TRUE if the process with number "b0p->b0_pid" is still running. + * "swap_fname" is the name of the swap file, if it's from before a reboot then + * the result is FALSE; + */ + static int +swapfile_process_running(ZERO_BL *b0p, char_u *swap_fname UNUSED) +{ +#ifdef HAVE_SYSINFO_UPTIME + stat_T st; + struct sysinfo sinfo; + + // If the system rebooted after when the swap file was written then the + // process can't be running now. + if (mch_stat((char *)swap_fname, &st) != -1 + && sysinfo(&sinfo) == 0 + && st.st_mtime < time(NULL) - ( +# ifdef FEAT_EVAL + override_sysinfo_uptime >= 0 ? override_sysinfo_uptime : +# endif + sinfo.uptime)) + return FALSE; +#endif + return mch_process_running(char_to_long(b0p->b0_pid)); +} /* * Try to recover curbuf from the .swp file. @@ -1692,7 +1721,7 @@ ml_recover(int checkext) msg(_("Recovery completed. Buffer contents equals file contents.")); msg_puts(_("\nYou may want to delete the .swp file now.")); #if defined(UNIX) || defined(MSWIN) - if (mch_process_running(char_to_long(b0p->b0_pid))) + if (swapfile_process_running(b0p, fname_used)) { // Warn there could be an active Vim on the same file, the user may // want to kill it. @@ -2170,7 +2199,7 @@ swapfile_info(char_u *fname) msg_puts(_("\n process ID: ")); msg_outnum(char_to_long(b0.b0_pid)); #if defined(UNIX) || defined(MSWIN) - if (mch_process_running(char_to_long(b0.b0_pid))) + if (swapfile_process_running(&b0, fname)) { msg_puts(_(" (STILL RUNNING)")); # ifdef HAVE_PROCESS_STILL_RUNNING @@ -2213,9 +2242,6 @@ swapfile_unchanged(char_u *fname) int fd; struct block0 b0; int ret = TRUE; -#if defined(UNIX) || defined(MSWIN) - long pid; -#endif // must be able to stat the swap file if (mch_stat((char *)fname, &st) == -1) @@ -2258,8 +2284,7 @@ swapfile_unchanged(char_u *fname) } // process must be known and not be running - pid = char_to_long(b0.b0_pid); - if (pid == 0L || mch_process_running(pid)) + if (char_to_long(b0.b0_pid) == 0L || swapfile_process_running(&b0, fname)) ret = FALSE; #endif diff --git a/src/option.c b/src/option.c index 6d55089ac1..c06778dd74 100644 --- a/src/option.c +++ b/src/option.c @@ -3247,7 +3247,9 @@ set_bool_option( if (curwin->w_curswant != MAXCOL && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) curwin->w_set_curswant = TRUE; - check_redraw(options[opt_idx].flags); + + if ((opt_flags & OPT_NO_REDRAW) == 0) + check_redraw(options[opt_idx].flags); return NULL; } @@ -3263,8 +3265,8 @@ set_num_option( long value, // new value char *errbuf, // buffer for error messages size_t errbuflen, // length of "errbuf" - int opt_flags) // OPT_LOCAL, OPT_GLOBAL and - // OPT_MODELINE + int opt_flags) // OPT_LOCAL, OPT_GLOBAL, + // OPT_MODELINE, etc. { char *errmsg = NULL; long old_value = *(long *)varp; @@ -3842,7 +3844,8 @@ set_num_option( if (curwin->w_curswant != MAXCOL && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0) curwin->w_set_curswant = TRUE; - check_redraw(options[opt_idx].flags); + if ((opt_flags & OPT_NO_REDRAW) == 0) + check_redraw(options[opt_idx].flags); return errmsg; } diff --git a/src/optionstr.c b/src/optionstr.c index 96c7b39a44..85c4a76d4a 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -2480,11 +2480,14 @@ ambw_end: && (get_option_flags(opt_idx) & (P_CURSWANT | P_RALL)) != 0) curwin->w_set_curswant = TRUE; + if ((opt_flags & OPT_NO_REDRAW) == 0) + { #ifdef FEAT_GUI - // check redraw when it's not a GUI option or the GUI is active. - if (!redraw_gui_only || gui.in_use) + // check redraw when it's not a GUI option or the GUI is active. + if (!redraw_gui_only || gui.in_use) #endif - check_redraw(get_option_flags(opt_idx)); + check_redraw(get_option_flags(opt_idx)); + } #if defined(FEAT_VTP) && defined(FEAT_TERMGUICOLORS) if (did_swaptcap) diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro index bd2c6879ff..c75f80a863 100644 --- a/src/proto/evalvars.pro +++ b/src/proto/evalvars.pro @@ -56,12 +56,12 @@ void set_reg_var(int c); char_u *v_exception(char_u *oldval); char_u *v_throwpoint(char_u *oldval); char_u *set_cmdarg(exarg_T *eap, char_u *oldarg); -int eval_variable(char_u *name, int len, typval_T *rettv, dictitem_T **dip, int verbose, int no_autoload); +int eval_variable(char_u *name, int len, typval_T *rettv, dictitem_T **dip, int flags); void check_vars(char_u *name, int len); dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload); dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload); hashtab_T *get_script_local_ht(void); -int lookup_scriptitem(char_u *name, size_t len, cctx_T *dummy); +int lookup_scriptitem(char_u *name, size_t len, int cmd, cctx_T *dummy); hashtab_T *find_var_ht(char_u *name, char_u **varname); char_u *get_var_value(char_u *name); void new_script_vars(scid_T id); diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index a05e897922..e6057d87ad 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -13,7 +13,7 @@ void undo_cmdmod(cmdmod_T *cmod); int parse_cmd_address(exarg_T *eap, char **errormsg, int silent); int checkforcmd(char_u **pp, char *cmd, int len); char_u *skip_option_env_lead(char_u *start); -char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx); +char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, int cmd, cctx_T *), cctx_T *cctx); int modifier_len(char_u *cmd); int cmd_exists(char_u *name); void f_fullcommand(typval_T *argvars, typval_T *rettv); diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro index c6493519cf..c80618f750 100644 --- a/src/proto/vim9script.pro +++ b/src/proto/vim9script.pro @@ -7,7 +7,7 @@ void ex_export(exarg_T *eap); void free_imports_and_script_vars(int sid); void mark_imports_for_reload(int sid); void ex_import(exarg_T *eap); -int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx); +int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, int verbose); char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T *tv, type_T **type); diff --git a/src/scriptfile.c b/src/scriptfile.c index 82ae42a1a6..203cad09fd 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -1459,7 +1459,34 @@ almosttheend: si = SCRIPT_ITEM(current_sctx.sc_sid); if (si->sn_save_cpo != NULL) { - set_option_value((char_u *)"cpo", 0L, si->sn_save_cpo, 0); + if (STRCMP(p_cpo, CPO_VIM) != 0) + { + char_u *f; + char_u *t; + + // 'cpo' was changed in the script. Apply the same change to the + // saved value, if possible. + for (f = (char_u *)CPO_VIM; *f != NUL; ++f) + if (vim_strchr(p_cpo, *f) == NULL + && (t = vim_strchr(si->sn_save_cpo, *f)) != NULL) + // flag was removed, also remove it from the saved 'cpo' + mch_memmove(t, t + 1, STRLEN(t)); + for (f = p_cpo; *f != NUL; ++f) + if (vim_strchr((char_u *)CPO_VIM, *f) == NULL + && vim_strchr(si->sn_save_cpo, *f) == NULL) + { + // flag was added, also add it to the saved 'cpo' + t = alloc(STRLEN(si->sn_save_cpo) + 2); + if (t != NULL) + { + *t = *f; + STRCPY(t + 1, si->sn_save_cpo); + vim_free(si->sn_save_cpo); + si->sn_save_cpo = t; + } + } + } + set_option_value((char_u *)"cpo", 0L, si->sn_save_cpo, OPT_NO_REDRAW); VIM_CLEAR(si->sn_save_cpo); } diff --git a/src/session.c b/src/session.c index 84ae998416..14a611078c 100644 --- a/src/session.c +++ b/src/session.c @@ -401,11 +401,12 @@ put_view( { buf_T *alt = buflist_findnr(wp->w_alt_fnum); - // Set the alternate file. + // Set the alternate file if the buffer is listed. if ((flagp == &ssop_flags) && alt != NULL && alt->b_fname != NULL && *alt->b_fname != NUL + && alt->b_p_bl && (fputs("balt ", fd) < 0 || ses_fname(fd, alt, flagp, TRUE) == FAIL)) return FAIL; diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index a2194f1686..6dabb51a18 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -461,6 +461,7 @@ NEW_TESTS_RES = \ test_quickfix.res \ test_quotestar.res \ test_random.res \ + test_recover.res \ test_regex_char_classes.res \ test_registers.res \ test_rename.res \ diff --git a/src/testdir/check.vim b/src/testdir/check.vim index cd0a8b75af..787bf2808a 100644 --- a/src/testdir/check.vim +++ b/src/testdir/check.vim @@ -84,8 +84,16 @@ func CheckUnix() endif endfunc +" Command to check for running on Linix +command CheckLinux call CheckLinux() +func CheckLinux() + if !has('linux') + throw 'Skipped: only works on Linux' + endif +endfunc + " Command to check for not running on a BSD system. -" TODO: using this checks should not be needed +" TODO: using this check should not be needed command CheckNotBSD call CheckNotBSD() func CheckNotBSD() if has('bsd') @@ -191,4 +199,29 @@ func CheckNotAsan() endif endfunc +" Command to check for satisfying any of the conditions. +" e.g. CheckAnyOf Feature:bsd Feature:sun Linux +command -nargs=+ CheckAnyOf call CheckAnyOf() +func CheckAnyOf(...) + let excp = [] + for arg in a:000 + try + exe 'Check' .. substitute(arg, ':', ' ', '') + return + catch /^Skipped:/ + let excp += [substitute(v:exception, '^Skipped:\s*', '', '')] + endtry + endfor + throw 'Skipped: ' .. join(excp, '; ') +endfunc + +" Command to check for satisfying all of the conditions. +" e.g. CheckAllOf Unix Gui Option:ballooneval +command -nargs=+ CheckAllOf call CheckAllOf() +func CheckAllOf(...) + for arg in a:000 + exe 'Check' .. substitute(arg, ':', ' ', '') + endfor +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/dumps/Test_vim9_no_redraw.dump b/src/testdir/dumps/Test_vim9_no_redraw.dump new file mode 100644 index 0000000000..1d77a08d07 --- /dev/null +++ b/src/testdir/dumps/Test_vim9_no_redraw.dump @@ -0,0 +1,6 @@ +|s+0&#ffffff0|o+0&#e0e0e08|m|e| |t|e|x|t| | +0&#ffffff0@64 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|:+0#0000000&|'|<|,|'|>> @68 diff --git a/src/testdir/runtest.vim b/src/testdir/runtest.vim index 328c5ad56f..06ed97932b 100644 --- a/src/testdir/runtest.vim +++ b/src/testdir/runtest.vim @@ -196,7 +196,12 @@ func RunTheTest(test) if a:test =~ 'Test_nocatch_' " Function handles errors itself. This avoids skipping commands after the " error. + let g:skipped_reason = '' exe 'call ' . a:test + if g:skipped_reason != '' + call add(s:messages, ' Skipped') + call add(s:skipped, 'SKIPPED ' . a:test . ': ' . g:skipped_reason) + endif else try au VimLeavePre * call EarlyExit(g:testfunc) diff --git a/src/testdir/test_alot.vim b/src/testdir/test_alot.vim index 83f2c1923c..f54e6f2c50 100644 --- a/src/testdir/test_alot.vim +++ b/src/testdir/test_alot.vim @@ -21,7 +21,6 @@ source test_jumps.vim source test_lispwords.vim source test_move.vim source test_put.vim -source test_recover.vim source test_reltime.vim source test_scroll_opt.vim source test_searchpos.vim diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 5839691aa9..f6e9b49f93 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -2670,6 +2670,9 @@ func Test_autocmd_window() %bw! edit one.txt tabnew two.txt + vnew three.txt + tabnew four.txt + tabprevious let g:blist = [] augroup aucmd_win_test1 au! @@ -2678,7 +2681,12 @@ func Test_autocmd_window() augroup END doautoall BufEnter - call assert_equal([['one.txt', 'autocmd'], ['two.txt', '']], g:blist) + call assert_equal([ + \ ['one.txt', 'autocmd'], + \ ['two.txt', ''], + \ ['four.txt', 'autocmd'], + \ ['three.txt', ''], + \ ], g:blist) augroup aucmd_win_test1 au! diff --git a/src/testdir/test_expr_utf8.vim b/src/testdir/test_expr_utf8.vim index b5937b2087..c6d2e4ed7e 100644 --- a/src/testdir/test_expr_utf8.vim +++ b/src/testdir/test_expr_utf8.vim @@ -31,6 +31,14 @@ func Test_strcharpart() call assert_equal('a', strcharpart('àxb', 0, 1)) call assert_equal('̀', strcharpart('àxb', 1, 1)) call assert_equal('x', strcharpart('àxb', 2, 1)) + + + call assert_equal('a', strcharpart('àxb', 0, 1, 0)) + call assert_equal('à', strcharpart('àxb', 0, 1, 1)) + call assert_equal('x', strcharpart('àxb', 1, 1, 1)) + + call assert_fails("let v = strcharpart('abc', 0, 0, [])", 'E745:') + call assert_fails("let v = strcharpart('abc', 0, 0, 2)", 'E1023:') endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_fileformat.vim b/src/testdir/test_fileformat.vim index 09be258221..07819dc2e6 100644 --- a/src/testdir/test_fileformat.vim +++ b/src/testdir/test_fileformat.vim @@ -33,6 +33,15 @@ func Test_fileformat_autocommand() bw! endfunc +func Test_fileformat_nomodifiable() + new + setlocal nomodifiable + + call assert_fails('set fileformat=latin1', 'E21:') + + bw +endfunc + " Convert the contents of a file into a literal string func s:file2str(fname) let b = readfile(a:fname, 'B') diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index ba8879c82d..11b6baeb83 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -374,6 +374,7 @@ let s:filename_checks = { \ 'po': ['file.po', 'file.pot'], \ 'pod': ['file.pod'], \ 'pod6': ['file.pod6'], + \ 'poke': ['file.pk'], \ 'postscr': ['file.ps', 'file.pfa', 'file.afm', 'file.eps', 'file.epsf', 'file.epsi', 'file.ai'], \ 'pov': ['file.pov'], \ 'povini': ['.povrayrc'], diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index 64afe54817..ca57c3086b 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -1430,6 +1430,14 @@ func Test_input_func() delfunc Tcomplete call assert_equal('item1 item2 item3', c) + " Test for using special characters as default input + call feedkeys(":let c = input('name? ', \"x\y\")\\", 'xt') + call assert_equal('y', c) + + " Test for using as default input + call feedkeys(":let c = input('name? ', \"\\\")\x\", 'xt') + call assert_equal(' x', c) + call assert_fails("call input('F:', '', 'invalid')", 'E180:') call assert_fails("call input('F:', '', [])", 'E730:') endfunc diff --git a/src/testdir/test_lua.vim b/src/testdir/test_lua.vim index 43b4aa30ff..7de4574530 100644 --- a/src/testdir/test_lua.vim +++ b/src/testdir/test_lua.vim @@ -121,6 +121,15 @@ func Test_lua_eval() lua v = nil endfunc +" Test luaeval() with lambda +func Test_luaeval_with_lambda() + lua function hello_luaeval_lambda(a, cb) return a .. cb() end + call assert_equal('helloworld', + \ luaeval('hello_luaeval_lambda(_A[1], _A[2])', + \ ['hello', {->'world'}])) + lua hello_luaeval_lambda = nil +endfunc + " Test vim.window() func Test_lua_window() e Xfoo2 diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim index 1750f39d53..741e31520b 100644 --- a/src/testdir/test_mapping.vim +++ b/src/testdir/test_mapping.vim @@ -485,6 +485,30 @@ func Test_list_mappings() nmapclear endfunc +func Test_expr_map_gets_cursor() + new + call setline(1, ['one', 'some w!rd']) + func StoreColumn() + let g:exprLine = line('.') + let g:exprCol = col('.') + return 'x' + endfunc + nnoremap x StoreColumn() + 2 + nmap ! f!x + call feedkeys("!", 'xt') + call assert_equal('some wrd', getline(2)) + call assert_equal(2, g:exprLine) + call assert_equal(7, g:exprCol) + + bwipe! + unlet g:exprLine + unlet g:exprCol + delfunc StoreColumn + nunmap x + nunmap ! +endfunc + func Test_expr_map_restore_cursor() CheckScreendump diff --git a/src/testdir/test_memory_usage.vim b/src/testdir/test_memory_usage.vim index 4f78d9a094..3b7b37247f 100644 --- a/src/testdir/test_memory_usage.vim +++ b/src/testdir/test_memory_usage.vim @@ -147,9 +147,9 @@ func Test_memory_func_capture_lvars() " The usage may be a bit less than the last value, use 80%. " Allow for 20% tolerance at the upper limit. That's very permissive, but " otherwise the test fails sometimes. On Cirrus CI with FreeBSD we need to - " be even more permissive. + " be even much more permissive. if has('bsd') - let multiplier = 15 + let multiplier = 19 else let multiplier = 12 endif diff --git a/src/testdir/test_messages.vim b/src/testdir/test_messages.vim index 4f8c75112f..375cb01461 100644 --- a/src/testdir/test_messages.vim +++ b/src/testdir/test_messages.vim @@ -259,6 +259,17 @@ func Test_message_more() call term_sendkeys(buf, 'q') call WaitForAssert({-> assert_equal('100', term_getline(buf, 5))}) + " Execute a : command from the more prompt + call term_sendkeys(buf, ":%p#\n") + call term_wait(buf) + call WaitForAssert({-> assert_equal('-- More --', term_getline(buf, 6))}) + call term_sendkeys(buf, ":") + call term_wait(buf) + call WaitForAssert({-> assert_equal(':', term_getline(buf, 6))}) + call term_sendkeys(buf, "echo 'Hello'\n") + call term_wait(buf) + call WaitForAssert({-> assert_equal('Hello ', term_getline(buf, 5))}) + call StopVimInTerminal(buf) endfunc diff --git a/src/testdir/test_mksession.vim b/src/testdir/test_mksession.vim index eb8c6a5a46..6e36553733 100644 --- a/src/testdir/test_mksession.vim +++ b/src/testdir/test_mksession.vim @@ -544,6 +544,20 @@ func Test_mkview_no_balt() %bwipe endfunc +func Test_mksession_no_balt() + edit Xtestfile1 + edit Xtestfile2 + + bdelete Xtestfile1 + mksession! Xtestview + + source Xtestview + call assert_equal(0, buflisted('Xtestfile1')) + + call delete('Xtestview') + %bwipe +endfunc + " Test :mkview with a file argument. func Test_mkview_file() " Create a view with line number and a fold. diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim index 8aef41ddda..75b52bcb6e 100644 --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -3207,6 +3207,13 @@ func Test_normal_delete_cmd() " delete to a readonly register call setline(1, ['abcd']) call assert_beeps('normal ":d2l') + + " D and d with 'nomodifiable' + call setline(1, ['abcd']) + setlocal nomodifiable + call assert_fails('normal D', 'E21:') + call assert_fails('normal d$', 'E21:') + close! endfunc diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index ba87d6aed8..88f92a4885 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -1038,6 +1038,27 @@ func Test_opt_winminheight_term() call delete('Xwinminheight') endfunc +func Test_opt_winminheight_term_tabs() + CheckRunVimInTerminal + + " The tabline should be taken into account. + let lines =<< trim END + set wmh=0 stal=2 + split + split + split + split + tabnew + END + call writefile(lines, 'Xwinminheight') + let buf = RunVimInTerminal('-S Xwinminheight', #{rows: 11}) + call term_sendkeys(buf, ":set wmh=1\n") + call WaitForAssert({-> assert_match('E36: Not enough room', term_getline(buf, 11))}) + + call StopVimInTerminal(buf) + call delete('Xwinminheight') +endfunc + " Test for the 'winminwidth' option func Test_opt_winminwidth() only! diff --git a/src/testdir/test_paste.vim b/src/testdir/test_paste.vim index 8995b971ac..df732f156f 100644 --- a/src/testdir/test_paste.vim +++ b/src/testdir/test_paste.vim @@ -149,4 +149,18 @@ func Test_xrestore() bwipe! endfunc +" Test for 'pastetoggle' +func Test_pastetoggle() + new + set pastetoggle= + set nopaste + call feedkeys("iHello\", 'xt') + call assert_true(&paste) + call feedkeys("i\", 'xt') + call assert_false(&paste) + call assert_equal('Hello', getline(1)) + set pastetoggle& + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_recover.vim b/src/testdir/test_recover.vim index 8408ff3693..a1ff7d92af 100644 --- a/src/testdir/test_recover.vim +++ b/src/testdir/test_recover.vim @@ -1,5 +1,7 @@ " Test :recover +source check.vim + func Test_recover_root_dir() " This used to access invalid memory. split Xtest @@ -21,6 +23,21 @@ func Test_recover_root_dir() set dir& endfunc +" Make a copy of the current swap file to "Xswap". +" Return the name of the swap file. +func CopySwapfile() + preserve + " get the name of the swap file + let swname = split(execute("swapname"))[0] + let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '') + " make a copy of the swap file in Xswap + set binary + exe 'sp ' . swname + w! Xswap + set nobinary + return swname +endfunc + " Inserts 10000 lines with text to fill the swap file with two levels of pointer " blocks. Then recovers from the swap file and checks all text is restored. " @@ -37,15 +54,9 @@ func Test_swap_file() let i += 1 endwhile $delete - preserve - " get the name of the swap file - let swname = split(execute("swapname"))[0] - let swname = substitute(swname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '') - " make a copy of the swap file in Xswap - set binary - exe 'sp ' . swname - w! Xswap - set nobinary + + let swname = CopySwapfile() + new only! bwipe! Xtest @@ -67,4 +78,59 @@ func Test_swap_file() enew! | only endfunc +func Test_nocatch_process_still_running() + " sysinfo.uptime probably only works on Linux + if !has('linux') + let g:skipped_reason = 'only works on Linux' + return + endif + " the GUI dialog can't be handled + if has('gui_running') + let g:skipped_reason = 'only works in the terminal' + return + endif + + " don't intercept existing swap file here + au! SwapExists + + " Edit a file and grab its swapfile. + edit Xswaptest + call setline(1, ['a', 'b', 'c']) + let swname = CopySwapfile() + + " Forget we edited this file + new + only! + bwipe! Xswaptest + + call rename('Xswap', swname) + call feedkeys('e', 'tL') + redir => editOutput + edit Xswaptest + redir END + call assert_match('E325: ATTENTION', editOutput) + call assert_match('file name: .*Xswaptest', editOutput) + call assert_match('process ID: \d* (STILL RUNNING)', editOutput) + + " Forget we edited this file + new + only! + bwipe! Xswaptest + + " pretend we rebooted + call test_override("uptime", 0) + sleep 1 + + call rename('Xswap', swname) + call feedkeys('e', 'tL') + redir => editOutput + edit Xswaptest + redir END + call assert_match('E325: ATTENTION', editOutput) + call assert_notmatch('(STILL RUNNING)', editOutput) + + call test_override("ALL", 0) + call delete(swname) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_registers.vim b/src/testdir/test_registers.vim index b2916f4d06..c10af29466 100644 --- a/src/testdir/test_registers.vim +++ b/src/testdir/test_registers.vim @@ -709,4 +709,14 @@ func Test_insert_small_delete() bwipe! endfunc +" Record in insert mode using CTRL-O +func Test_record_in_insert_mode() + new + let @r = '' + call setline(1, ['foo']) + call feedkeys("i\qrbaz\q", 'xt') + call assert_equal('baz', @r) + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_selectmode.vim b/src/testdir/test_selectmode.vim index 3adb843cb8..3296f7e702 100644 --- a/src/testdir/test_selectmode.vim +++ b/src/testdir/test_selectmode.vim @@ -165,7 +165,10 @@ func Test_term_mouse_multiple_clicks_to_select_mode() let save_term = &term let save_ttymouse = &ttymouse call test_override('no_query_mouse', 1) - set mouse=a term=xterm mousetime=200 + + " 'mousetime' must be sufficiently large, or else the test is flaky when + " using a ssh connection with X forwarding; i.e. ssh -X. + set mouse=a term=xterm mousetime=1000 set selectmode=mouse new diff --git a/src/testdir/test_sleep.vim b/src/testdir/test_sleep.vim index f71855fd4b..a428f380b0 100644 --- a/src/testdir/test_sleep.vim +++ b/src/testdir/test_sleep.vim @@ -21,6 +21,7 @@ func! Test_sleep_bang() call s:assert_takes_longer('sl 50m', 50) call s:assert_takes_longer('sl! 50m', 50) call s:assert_takes_longer('1sleep', 1000) + call s:assert_takes_longer('normal 1gs', 1000) endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim index ded635a10f..8fc3fc1fac 100644 --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -109,9 +109,9 @@ func Test_pack_in_rtp_when_plugins_run() endfunc func Test_help_arg() - if !has('unix') && has('gui_running') - throw 'Skipped: does not work with gvim on MS-Windows' - endif + " This does not work with a GUI-only binary, such as on MS-Windows. + CheckAnyOf Unix NotGui + if RunVim([], [], '--help >Xtestout') let lines = readfile('Xtestout') call assert_true(len(lines) > 20) @@ -408,6 +408,134 @@ func Test_A_F_H_arg() call delete('Xtestout') endfunc +" Test the --echo-wid argument (for GTK GUI only). +func Test_echo_wid() + CheckCanRunGui + CheckFeature gui_gtk + + if RunVim([], [], '-g --echo-wid -cq >Xtest_echo_wid') + let lines = readfile('Xtest_echo_wid') + call assert_equal(1, len(lines)) + call assert_match('^WID: \d\+$', lines[0]) + endif + + call delete('Xtest_echo_wid') +endfunction + +" Test the -reverse and +reverse arguments (for GUI only). +func Test_reverse() + CheckCanRunGui + CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena + + let after =<< trim [CODE] + call writefile([&background], "Xtest_reverse") + qall + [CODE] + if RunVim([], after, '-f -g -reverse') + let lines = readfile('Xtest_reverse') + call assert_equal(['dark'], lines) + endif + if RunVim([], after, '-f -g +reverse') + let lines = readfile('Xtest_reverse') + call assert_equal(['light'], lines) + endif + + call delete('Xtest_reverse') +endfunc + +" Test the -background and -foreground arguments (for GUI only). +func Test_background_foreground() + CheckCanRunGui + CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena + + " Is there a better way to check the effect of -background & -foreground + " other than merely looking at &background (dark or light)? + let after =<< trim [CODE] + call writefile([&background], "Xtest_fg_bg") + qall + [CODE] + if RunVim([], after, '-f -g -background darkred -foreground yellow') + let lines = readfile('Xtest_fg_bg') + call assert_equal(['dark'], lines) + endif + if RunVim([], after, '-f -g -background ivory -foreground darkgreen') + let lines = readfile('Xtest_fg_bg') + call assert_equal(['light'], lines) + endif + + call delete('Xtest_fg_bg') +endfunc + +" Test the -font argument (for GUI only). +func Test_font() + CheckCanRunGui + CheckNotMSWindows + + if has('gui_gtk') + let font = 'Courier 14' + elseif has('gui_motif') || has('gui_athena') + let font = '-misc-fixed-bold-*' + else + throw 'Skipped: test does not set a valid font for this GUI' + endif + + let after =<< trim [CODE] + call writefile([&guifont], "Xtest_font") + qall + [CODE] + + if RunVim([], after, '--nofork -g -font "' .. font .. '"') + let lines = readfile('Xtest_font') + call assert_equal([font], lines) + endif + + call delete('Xtest_font') +endfunc + +" Test the -geometry argument (for GUI only). +func Test_geometry() + CheckCanRunGui + CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena + + if has('gui_motif') || has('gui_athena') + " FIXME: With GUI Athena or Motif, the value of getwinposx(), + " getwinposy() and getwinpos() do not match exactly the + " value given in -geometry. Why? + " So only check &columns and &lines for those GUIs. + let after =<< trim [CODE] + call writefile([&columns, &lines], "Xtest_geometry") + qall + [CODE] + if RunVim([], after, '-f -g -geometry 31x13+41+43') + let lines = readfile('Xtest_geometry') + call assert_equal(['31', '13'], lines) + endif + else + let after =<< trim [CODE] + call writefile([&columns, &lines, getwinposx(), getwinposy(), string(getwinpos())], "Xtest_geometry") + qall + [CODE] + if RunVim([], after, '-f -g -geometry 31x13+41+43') + let lines = readfile('Xtest_geometry') + call assert_equal(['31', '13', '41', '43', '[41, 43]'], lines) + endif + endif + + call delete('Xtest_geometry') +endfunc + +" Test the -iconic argument (for GUI only). +func Test_iconic() + CheckCanRunGui + CheckAnyOf Feature:gui_gtk Feature:gui_motif Feature:gui_athena + + call RunVim([], [], '-f -g -iconic -cq') + + " TODO: currently only start vim iconified, but does not + " check that vim is iconified. How could this be checked? +endfunc + + func Test_invalid_args() " must be able to get the output of Vim. CheckUnix @@ -1044,16 +1172,11 @@ func Test_progname() \ 'vimdiff', 'gvimdiff'] for progname in prognames - if empty($DISPLAY) - if progname =~# 'g' - " Can't run gvim, gview (etc.) if $DISPLAY is not setup. - continue - endif - if has('gui') && (progname ==# 'evim' || progname ==# 'eview') - " evim or eview will start the GUI if there is gui support. - " So don't try to start them either if $DISPLAY is not setup. - continue - endif + let run_with_gui = (progname =~# 'g') || (has('gui') && (progname ==# 'evim' || progname ==# 'eview')) + + if empty($DISPLAY) && run_with_gui + " Can't run gvim, gview (etc.) if $DISPLAY is not setup. + continue endif exe 'silent !ln -s -f ' ..exepath(GetVimProg()) .. ' Xprogname/' .. progname @@ -1068,7 +1191,15 @@ func Test_progname() if progname =~# 'g' && !has('gui') call assert_equal("E25: GUI cannot be used: Not enabled at compile time\n", stdout_stderr, progname) else - call assert_equal('', stdout_stderr, progname) + " GUI motif can output some warnings like this: + " Warning: + " Name: subMenu + " Class: XmCascadeButton + " Illegal mnemonic character; Could not convert X KEYSYM to a keycode + " So don't check that stderr is empty with GUI Motif. + if run_with_gui && !has('gui_motif') + call assert_equal('', stdout_stderr, progname) + endif call assert_equal(expectations[progname], readfile('Xprogname_out'), progname) endif diff --git a/src/testdir/test_textformat.vim b/src/testdir/test_textformat.vim index eac1c1980a..32ff3cc70b 100644 --- a/src/testdir/test_textformat.vim +++ b/src/testdir/test_textformat.vim @@ -858,6 +858,21 @@ func Test_mps_latin1() close! endfunc +func Test_mps_error() + let encoding_save = &encoding + + for e in ['utf-8', 'latin1'] + exe 'set encoding=' .. e + + call assert_fails('set mps=<:', 'E474:', e) + call assert_fails('set mps=:>', 'E474:', e) + call assert_fails('set mps=<>', 'E474:', e) + call assert_fails('set mps=<:>_', 'E474:', e) + endfor + + let &encoding = encoding_save +endfunc + " Test for ra on multi-byte characters func Test_ra_multibyte() new diff --git a/src/testdir/test_undo.vim b/src/testdir/test_undo.vim index dd4b902b93..f12e1525c4 100644 --- a/src/testdir/test_undo.vim +++ b/src/testdir/test_undo.vim @@ -733,4 +733,18 @@ func Test_undofile_cryptmethod_blowfish2() set undofile& undolevels& cryptmethod& endfunc +" Test for redoing with incrementing numbered registers +func Test_redo_repeat_numbered_register() + new + for [i, v] in [[1, 'one'], [2, 'two'], [3, 'three'], + \ [4, 'four'], [5, 'five'], [6, 'six'], + \ [7, 'seven'], [8, 'eight'], [9, 'nine']] + exe 'let @' .. i .. '="' .. v .. '\n"' + endfor + call feedkeys('"1p.........', 'xt') + call assert_equal(['', 'one', 'two', 'three', 'four', 'five', 'six', + \ 'seven', 'eight', 'nine', 'nine'], getline(1, '$')) + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_utf8.vim b/src/testdir/test_utf8.vim index a5fd4fb89a..5454e430ad 100644 --- a/src/testdir/test_utf8.vim +++ b/src/testdir/test_utf8.vim @@ -11,7 +11,7 @@ func Test_visual_block_insert() bwipeout! endfunc -" Test for built-in function strchars() +" Test for built-in functions strchars() and strcharlen() func Test_strchars() let inp = ["a", "あいa", "A\u20dd", "A\u20dd\u20dd", "\u20dd"] let exp = [[1, 1, 1], [3, 3, 3], [2, 2, 1], [3, 3, 1], [1, 1, 1]] @@ -20,6 +20,13 @@ func Test_strchars() call assert_equal(exp[i][1], inp[i]->strchars(0)) call assert_equal(exp[i][2], strchars(inp[i], 1)) endfor + + let exp = [1, 3, 1, 1, 1] + for i in range(len(inp)) + call assert_equal(exp[i], inp[i]->strcharlen()) + call assert_equal(exp[i], strcharlen(inp[i])) + endfor + call assert_fails("let v=strchars('abc', [])", 'E745:') call assert_fails("let v=strchars('abc', 2)", 'E1023:') endfunc diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 23c0a89c85..f71226e1bd 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -72,6 +72,13 @@ def Test_assignment() CheckDefFailure(['var lambda = () => "lambda"'], 'E704:') CheckScriptFailure(['var x = "x"'], 'E1124:') + # lower case name is OK for a list + var lambdaLines =<< trim END + var lambdaList: list = [Test_syntax] + lambdaList[0] = () => "lambda" + END + CheckDefAndScriptSuccess(lambdaLines) + var nr: number = 1234 CheckDefFailure(['var nr: number = "asdf"'], 'E1012:') diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 3ee8a620f6..75bbc0c506 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -340,6 +340,26 @@ def Test_extend_list_item_type() CheckScriptFailure(['vim9script'] + lines, 'E1012:', 1) enddef +def Test_extend_with_error_function() + var lines =<< trim END + vim9script + def F() + { + var m = 10 + } + echo m + enddef + + def Test() + var d: dict = {} + d->extend({A: 10, Func: function('F', [])}) + enddef + + Test() + END + CheckScriptFailure(lines, 'E1001: Variable not found: m') +enddef + def Test_job_info_return_type() if has('job') job_start(&shell) @@ -472,6 +492,19 @@ def Test_getchar() getchar(true)->assert_equal(0) enddef +def Test_getenv() + if getenv('does-not_exist') == '' + assert_report('getenv() should return null') + endif + if getenv('does-not_exist') == null + else + assert_report('getenv() should return null') + endif + $SOMEENVVAR = 'some' + assert_equal('some', getenv('SOMEENVVAR')) + unlet $SOMEENVVAR +enddef + def Test_getcompletion() set wildignore=*.vim,*~ var l = getcompletion('run', 'file', true) diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index dd6e966de1..c21e55a477 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -364,9 +364,8 @@ def Test_method_call_linebreak() return F() enddef def Test() - Foo - ->Bar() - ->setline(1) + Foo ->Bar() + ->setline(1) enddef Test() assert_equal('the text', getline(1)) @@ -401,8 +400,7 @@ def Test_method_call_linebreak() return F() enddef - Foo - ->Bar() + Foo->Bar() ->setline(1) END CheckScriptSuccess(lines) @@ -424,6 +422,33 @@ def Test_method_call_whitespace() CheckDefAndScriptSuccess(lines) enddef +def Test_method_and_user_command() + var lines =<< trim END + vim9script + def Cmd() + g:didFunc = 1 + enddef + command Cmd g:didCmd = 1 + Cmd + assert_equal(1, g:didCmd) + Cmd() + assert_equal(1, g:didFunc) + unlet g:didFunc + unlet g:didCmd + + def InDefFunc() + Cmd + assert_equal(1, g:didCmd) + Cmd() + assert_equal(1, g:didFunc) + unlet g:didFunc + unlet g:didCmd + enddef + InDefFunc() + END + CheckScriptSuccess(lines) +enddef + def Test_skipped_expr_linebreak() if 0 var x = [] diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index 9d05bbfd22..7470ac3ce7 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -947,7 +947,7 @@ def NestedOuter() enddef enddef -def Test_nested_func() +def Test_disassemble_nested_func() var instr = execute('disassemble NestedOuter') assert_match('NestedOuter\_s*' .. 'def g:Inner()\_s*' .. @@ -965,7 +965,7 @@ def NestedDefList() def /Info/ enddef -def Test_nested_def_list() +def Test_disassemble_nested_def_list() var instr = execute('disassemble NestedDefList') assert_match('NestedDefList\_s*' .. 'def\_s*' .. diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index a55de7e83a..9829d88007 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1370,6 +1370,9 @@ def Test_expr5_list_add() dany[i] = i endfor assert_equal({a: 'a', 12: 12}, dany) + + # result of glob() is "any", runtime type check + var sl: list = glob('*.txt', false, true) + [''] enddef " test multiply, divide, modulo @@ -2364,6 +2367,35 @@ def Test_expr7_any_index_slice() assert_equal('abcd', g:teststring[: -3]) assert_equal('', g:teststring[: -9]) + # composing characters are included + g:teststring = 'àéû' + assert_equal('à', g:teststring[0]) + assert_equal('é', g:teststring[1]) + assert_equal('û', g:teststring[2]) + assert_equal('', g:teststring[3]) + assert_equal('', g:teststring[4]) + + assert_equal('û', g:teststring[-1]) + assert_equal('é', g:teststring[-2]) + assert_equal('à', g:teststring[-3]) + assert_equal('', g:teststring[-4]) + assert_equal('', g:teststring[-5]) + + assert_equal('à', g:teststring[0 : 0]) + assert_equal('é', g:teststring[1 : 1]) + assert_equal('àé', g:teststring[0 : 1]) + assert_equal('àéû', g:teststring[0 : -1]) + assert_equal('àé', g:teststring[0 : -2]) + assert_equal('à', g:teststring[0 : -3]) + assert_equal('', g:teststring[0 : -4]) + assert_equal('', g:teststring[0 : -5]) + assert_equal('àéû', g:teststring[ : ]) + assert_equal('àéû', g:teststring[0 : ]) + assert_equal('éû', g:teststring[1 : ]) + assert_equal('û', g:teststring[2 : ]) + assert_equal('', g:teststring[3 : ]) + assert_equal('', g:teststring[4 : ]) + # blob index cannot be out of range g:testblob = 0z01ab assert_equal(0x01, g:testblob[0]) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 810b3a3cf0..2697c29e44 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -311,6 +311,14 @@ def Test_call_default_args() delfunc g:Func CheckScriptFailure(['def Func(arg: number = "text")', 'enddef', 'defcompile'], 'E1013: Argument 1: type mismatch, expected number but got string') delfunc g:Func + + var lines =<< trim END + vim9script + def Func(a = b == 0 ? 1 : 2, b = 0) + enddef + defcompile + END + CheckScriptFailure(lines, 'E1001: Variable not found: b') enddef def FuncWithComment( # comment @@ -385,7 +393,6 @@ def Test_nested_function() CheckDefFailure(lines, 'E1117:') # nested function inside conditional - # TODO: should it work when "thecount" is inside the "if"? lines =<< trim END vim9script var thecount = 0 @@ -403,6 +410,25 @@ def Test_nested_function() assert_equal(2, Test()) END CheckScriptSuccess(lines) + + # also works when "thecount" is inside the "if" block + lines =<< trim END + vim9script + if true + var thecount = 0 + def Test(): number + def TheFunc(): number + thecount += 1 + return thecount + enddef + return TheFunc() + enddef + endif + defcompile + assert_equal(1, Test()) + assert_equal(2, Test()) + END + CheckScriptSuccess(lines) enddef def Test_not_nested_function() @@ -726,11 +752,26 @@ def Test_call_lambda_args() CheckDefFailure(lines, 'E1167:') enddef +def FilterWithCond(x: string, Cond: func(string): bool): bool + return Cond(x) +enddef + def Test_lambda_return_type() var lines =<< trim END var Ref = (): => 123 END CheckDefAndScriptFailure(lines, 'E1157:', 1) + + # this works + for x in ['foo', 'boo'] + echo FilterWithCond(x, (v) => v =~ '^b') + endfor + + # this fails + lines =<< trim END + echo FilterWithCond('foo', (v) => v .. '^b') + END + CheckDefAndScriptFailure(lines, 'E1013: Argument 2: type mismatch, expected func(string): bool but got func(any): string', 1) enddef def Test_lambda_uses_assigned_var() diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 2743668ee5..38d0b0abcd 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -5,6 +5,7 @@ source term_util.vim source view_util.vim source vim9.vim source shared.vim +source screendump.vim def Test_range_only() new @@ -591,6 +592,31 @@ def Test_try_catch_throw() assert_equal(4, ReturnInFinally()) enddef +" :while at the very start of a function that :continue jumps to +def TryContinueFunc() + while g:Count < 2 + g:sequence ..= 't' + try + echoerr 'Test' + catch + g:Count += 1 + g:sequence ..= 'c' + continue + endtry + g:sequence ..= 'e' + g:Count += 1 + endwhile +enddef + +def Test_continue_in_try_in_while() + g:Count = 0 + g:sequence = '' + TryContinueFunc() + assert_equal('tctc', g:sequence) + unlet g:Count + unlet g:sequence +enddef + def Test_nocatch_return_in_try() # return in try block returns normally def ReturnInTry(): string @@ -1030,13 +1056,17 @@ def Test_vim9_import_export() vim9script import * as Export from './Xexport.vim' def UseExport() - g:imported = Export.exported + g:imported_def = Export.exported enddef + g:imported_script = Export.exported + assert_equal(1, exists('Export.exported')) + assert_equal(0, exists('Export.notexported')) UseExport() END writefile(import_star_as_lines, 'Ximport.vim') source Ximport.vim - assert_equal(9883, g:imported) + assert_equal(9883, g:imported_def) + assert_equal(9883, g:imported_script) var import_star_as_lines_no_dot =<< trim END vim9script @@ -1071,6 +1101,22 @@ def Test_vim9_import_export() writefile(import_star_as_duplicated, 'Ximport.vim') assert_fails('source Ximport.vim', 'E1073:', '', 4, 'Ximport.vim') + var import_star_as_lines_script_no_dot =<< trim END + vim9script + import * as Export from './Xexport.vim' + g:imported_script = Export exported + END + writefile(import_star_as_lines_script_no_dot, 'Ximport.vim') + assert_fails('source Ximport.vim', 'E1029:') + + var import_star_as_lines_script_space_after_dot =<< trim END + vim9script + import * as Export from './Xexport.vim' + g:imported_script = Export. exported + END + writefile(import_star_as_lines_script_space_after_dot, 'Ximport.vim') + assert_fails('source Ximport.vim', 'E1074:') + var import_star_as_lines_missing_name =<< trim END vim9script import * as Export from './Xexport.vim' @@ -1204,17 +1250,23 @@ def Test_vim9_import_export() delete('Xexport.vim') # Check that in a Vim9 script 'cpo' is set to the Vim default. - set cpo&vi - var cpo_before = &cpo + # Flags added or removed are also applied to the restored value. + set cpo=abcd var lines =<< trim END vim9script g:cpo_in_vim9script = &cpo + set cpo+=f + set cpo-=c + g:cpo_after_vim9script = &cpo END writefile(lines, 'Xvim9_script') source Xvim9_script - assert_equal(cpo_before, &cpo) + assert_equal('fabd', &cpo) set cpo&vim assert_equal(&cpo, g:cpo_in_vim9script) + var newcpo = substitute(&cpo, 'c', '', '') .. 'f' + assert_equal(newcpo, g:cpo_after_vim9script) + delete('Xvim9_script') enddef @@ -1515,6 +1567,35 @@ def Test_script_reload_change_type() delete('Xreload.vim') enddef +" Define CallFunc so that the test can be compiled +command CallFunc echo 'nop' + +def Test_script_reload_from_function() + var lines =<< trim END + vim9script + + if exists('g:loaded') + finish + endif + g:loaded = 1 + delcommand CallFunc + command CallFunc Func() + def Func() + so XreloadFunc.vim + g:didTheFunc = 1 + enddef + END + writefile(lines, 'XreloadFunc.vim') + source XreloadFunc.vim + CallFunc + assert_equal(1, g:didTheFunc) + + delete('XreloadFunc.vim') + delcommand CallFunc + unlet g:loaded + unlet g:didTheFunc +enddef + def Test_script_var_shadows_function() var lines =<< trim END vim9script @@ -1859,6 +1940,8 @@ def Test_no_insert_xit() CheckScriptFailure(['vim9script', 'c'], 'E1100:') CheckScriptFailure(['vim9script', 'i = 1'], 'E488:') CheckScriptFailure(['vim9script', 'i'], 'E1100:') + CheckScriptFailure(['vim9script', 'o = 1'], 'E1100:') + CheckScriptFailure(['vim9script', 'o'], 'E1100:') CheckScriptFailure(['vim9script', 't'], 'E1100:') CheckScriptFailure(['vim9script', 't = 1'], 'E1100:') CheckScriptFailure(['vim9script', 'x = 1'], 'E1100:') @@ -3154,6 +3237,10 @@ def Test_vim9_autoload() return 'test' enddef g:some#name = 'name' + + def some#varargs(a1: string, ...l: list): string + return a1 .. l[0] .. l[1] + enddef END mkdir('Xdir/autoload', 'p') @@ -3166,6 +3253,8 @@ def Test_vim9_autoload() g:some#other = 'other' assert_equal('other', g:some#other) + assert_equal('abc', some#varargs('a', 'b', 'c')) + # upper case script name works lines =<< trim END vim9script @@ -3332,6 +3421,38 @@ def Test_restoring_cpo() set cpo&vim enddef +" Use :function so we can use Check commands +func Test_no_redraw_when_restoring_cpo() + CheckScreendump + CheckFeature timers + + let lines =<< trim END + vim9script + def script#func() + enddef + END + call mkdir('Xdir/autoload', 'p') + call writefile(lines, 'Xdir/autoload/script.vim') + + let lines =<< trim END + vim9script + set cpo+=M + exe 'set rtp^=' .. getcwd() .. '/Xdir' + au CmdlineEnter : ++once timer_start(0, () => script#func()) + setline(1, 'some text') + END + call writefile(lines, 'XTest_redraw_cpo') + let buf = RunVimInTerminal('-S XTest_redraw_cpo', {'rows': 6}) + call term_sendkeys(buf, "V:") + call VerifyScreenDump(buf, 'Test_vim9_no_redraw', {}) + + " clean up + call term_sendkeys(buf, "\u") + call StopVimInTerminal(buf) + call delete('XTest_redraw_cpo') + call delete('Xdir', 'rf') +endfunc + def Test_unset_any_variable() var lines =<< trim END diff --git a/src/testdir/test_viminfo.vim b/src/testdir/test_viminfo.vim index cb090ce5ce..ef74e09dbd 100644 --- a/src/testdir/test_viminfo.vim +++ b/src/testdir/test_viminfo.vim @@ -906,6 +906,10 @@ func Test_viminfo_oldfiles_newfile() call delete('Xviminfofile') call delete('Xviminfotest') call delete('Xnew-file.txt') + + let v:oldfiles = test_null_list() + call assert_equal("\nNo old files", execute('oldfiles')) + let &viminfo = save_viminfo let &viminfofile = save_viminfofile endfunc diff --git a/src/testing.c b/src/testing.c index 6d4f588f8d..df19b9eb49 100644 --- a/src/testing.c +++ b/src/testing.c @@ -970,6 +970,8 @@ f_test_override(typval_T *argvars, typval_T *rettv UNUSED) ui_delay_for_testing = val; else if (STRCMP(name, (char_u *)"term_props") == 0) reset_term_props_on_termresponse = val; + else if (STRCMP(name, (char_u *)"uptime") == 0) + override_sysinfo_uptime = val; else if (STRCMP(name, (char_u *)"ALL") == 0) { disable_char_avail_for_testing = FALSE; @@ -979,6 +981,7 @@ f_test_override(typval_T *argvars, typval_T *rettv UNUSED) no_query_mouse_for_testing = FALSE; ui_delay_for_testing = 0; reset_term_props_on_termresponse = FALSE; + override_sysinfo_uptime = -1; if (save_starting >= 0) { starting = save_starting; diff --git a/src/userfunc.c b/src/userfunc.c index e89b8858d3..4793ee5552 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1359,9 +1359,12 @@ func_remove(ufunc_T *fp) // function, so we can find the index when defining the function again. // Do remove it when it's a copy. if (fp->uf_def_status == UF_COMPILED && (fp->uf_flags & FC_COPY) == 0) + { fp->uf_flags |= FC_DEAD; - else - hash_remove(&func_hashtab, hi); + return FALSE; + } + hash_remove(&func_hashtab, hi); + fp->uf_flags |= FC_DELETED; return TRUE; } return FALSE; @@ -2134,11 +2137,23 @@ delete_script_functions(int sid) int changed = func_hashtab.ht_changed; fp->uf_flags |= FC_DEAD; - func_clear(fp, TRUE); - // When clearing a function another function can be cleared - // as a side effect. When that happens start over. - if (changed != func_hashtab.ht_changed) - break; + + if (fp->uf_calls > 0) + { + // Function is executing, don't free it but do remove + // it from the hashtable. + if (func_remove(fp)) + fp->uf_refcount--; + } + else + { + func_clear(fp, TRUE); + // When clearing a function another function can be + // cleared as a side effect. When that happens start + // over. + if (changed != func_hashtab.ht_changed) + break; + } } --todo; } @@ -4251,7 +4266,6 @@ ex_delfunction(exarg_T *eap) // do remove it from the hashtable. if (func_remove(fp)) fp->uf_refcount--; - fp->uf_flags |= FC_DELETED; } else func_clear_free(fp, FALSE); diff --git a/src/version.c b/src/version.c index ffff455ff8..aa9f15386e 100644 --- a/src/version.c +++ b/src/version.c @@ -765,6 +765,92 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2619, +/**/ + 2618, +/**/ + 2617, +/**/ + 2616, +/**/ + 2615, +/**/ + 2614, +/**/ + 2613, +/**/ + 2612, +/**/ + 2611, +/**/ + 2610, +/**/ + 2609, +/**/ + 2608, +/**/ + 2607, +/**/ + 2606, +/**/ + 2605, +/**/ + 2604, +/**/ + 2603, +/**/ + 2602, +/**/ + 2601, +/**/ + 2600, +/**/ + 2599, +/**/ + 2598, +/**/ + 2597, +/**/ + 2596, +/**/ + 2595, +/**/ + 2594, +/**/ + 2593, +/**/ + 2592, +/**/ + 2591, +/**/ + 2590, +/**/ + 2589, +/**/ + 2588, +/**/ + 2587, +/**/ + 2586, +/**/ + 2585, +/**/ + 2584, +/**/ + 2583, +/**/ + 2582, +/**/ + 2581, +/**/ + 2580, +/**/ + 2579, +/**/ + 2578, +/**/ + 2577, /**/ 2576, /**/ diff --git a/src/vim.h b/src/vim.h index 817e277860..9a5aafe35e 100644 --- a/src/vim.h +++ b/src/vim.h @@ -1208,6 +1208,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring); #define OPT_WINONLY 0x10 // only set window-local options #define OPT_NOWIN 0x20 // don't set window-local options #define OPT_ONECOLUMN 0x40 // list options one per line +#define OPT_NO_REDRAW 0x80 // ignore redraw flags on option // Magic chars used in confirm dialog strings #define DLG_BUTTON_SEP '\n' @@ -2712,4 +2713,9 @@ long elapsed(DWORD start_tick); #define MCH_DELAY_IGNOREINPUT 1 #define MCH_DELAY_SETTMODE 2 +// Flags for eval_variable(). +#define EVAL_VAR_VERBOSE 1 // may give error message +#define EVAL_VAR_NOAUTOLOAD 2 // do not use script autoloading +#define EVAL_VAR_IMPORT 4 // may return special variable for import + #endif // VIM__H diff --git a/src/vim9compile.c b/src/vim9compile.c index c781060237..458b4a1a34 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -391,19 +391,29 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx) * imported or function. */ static int -item_exists(char_u *name, size_t len, cctx_T *cctx) +item_exists(char_u *name, size_t len, int cmd UNUSED, cctx_T *cctx) { int is_global; + char_u *p; if (variable_exists(name, len, cctx)) return TRUE; - // Find a function, so that a following "->" works. Skip "g:" before a - // function name. - // Do not check for an internal function, since it might also be a - // valid command, such as ":split" versuse "split()". - is_global = (name[0] == 'g' && name[1] == ':'); - return find_func(is_global ? name + 2 : name, is_global, cctx) != NULL; + // This is similar to what is in lookup_scriptitem(): + // Find a function, so that a following "->" works. + // Require "(" or "->" to follow, "Cmd" is a user command while "Cmd()" is + // a function call. + p = skipwhite(name + len); + + if (name[len] == '(' || (p[0] == '-' && p[1] == '>')) + { + // Do not check for an internal function, since it might also be a + // valid command, such as ":split" versuse "split()". + // Skip "g:" before a function name. + is_global = (name[0] == 'g' && name[1] == ':'); + return find_func(is_global ? name + 2 : name, is_global, cctx) != NULL; + } + return FALSE; } /* @@ -2645,7 +2655,8 @@ compile_load_scriptvar( cc = *p; *p = NUL; - idx = find_exported(import->imp_sid, exp_name, &ufunc, &type, cctx); + idx = find_exported(import->imp_sid, exp_name, &ufunc, &type, + cctx, TRUE); *p = cc; p = skipwhite(p); @@ -4184,7 +4195,7 @@ compile_expr7( * "null" constant */ case 'n': if (STRNCMP(*arg, "null", 4) == 0 - && !eval_isnamec((*arg)[5])) + && !eval_isnamec((*arg)[4])) { *arg += 4; rettv->v_type = VAR_SPECIAL; @@ -4450,7 +4461,7 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) } /* - * + number addition + * + number addition or list/blobl concatenation * - number subtraction * .. string concatenation */ @@ -4532,6 +4543,7 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) else { generate_ppconst(cctx, ppconst); + ppconst->pp_is_const = FALSE; if (*op == '.') { if (may_generate_2STRING(-2, cctx) == FAIL @@ -5171,6 +5183,21 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx) r = eap->skip ? OK : FAIL; goto theend; } + + // copy over the block scope IDs before compiling + if (!is_global && cctx->ctx_ufunc->uf_block_depth > 0) + { + int block_depth = cctx->ctx_ufunc->uf_block_depth; + + ufunc->uf_block_ids = ALLOC_MULT(int, block_depth); + if (ufunc->uf_block_ids != NULL) + { + mch_memmove(ufunc->uf_block_ids, cctx->ctx_ufunc->uf_block_ids, + sizeof(int) * block_depth); + ufunc->uf_block_depth = block_depth; + } + } + if (func_needs_compiling(ufunc, PROFILING(ufunc)) && compile_def_function(ufunc, TRUE, PROFILING(ufunc), cctx) == FAIL) @@ -5197,25 +5224,12 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx) // Define a local variable for the function reference. lvar_T *lvar = reserve_local(cctx, name_start, name_end - name_start, TRUE, ufunc->uf_func_type); - int block_depth = cctx->ctx_ufunc->uf_block_depth; if (lvar == NULL) goto theend; if (generate_FUNCREF(cctx, ufunc) == FAIL) goto theend; r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); - - // copy over the block scope IDs - if (block_depth > 0) - { - ufunc->uf_block_ids = ALLOC_MULT(int, block_depth); - if (ufunc->uf_block_ids != NULL) - { - mch_memmove(ufunc->uf_block_ids, cctx->ctx_ufunc->uf_block_ids, - sizeof(int) * block_depth); - ufunc->uf_block_depth = block_depth; - } - } } // TODO: warning for trailing text? @@ -5818,11 +5832,13 @@ compile_lhs( return FAIL; } - // new local variable + // Check the name is valid for a funcref. if ((lhs->lhs_type->tt_type == VAR_FUNC || lhs->lhs_type->tt_type == VAR_PARTIAL) - && var_wrong_func_name(lhs->lhs_name, TRUE)) + && var_wrong_func_name(lhs->lhs_name, TRUE)) return FAIL; + + // New local variable. lhs->lhs_lvar = reserve_local(cctx, var_start, lhs->lhs_varlen, cmdidx == CMD_final || cmdidx == CMD_const, lhs->lhs_type); if (lhs->lhs_lvar == NULL) @@ -6261,6 +6277,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) { if ((rhs_type->tt_type == VAR_FUNC || rhs_type->tt_type == VAR_PARTIAL) + && !lhs.lhs_has_index && var_wrong_func_name(lhs.lhs_name, TRUE)) goto theend; @@ -8198,6 +8215,7 @@ compile_def_function( { int count = ufunc->uf_def_args.ga_len; int first_def_arg = ufunc->uf_args.ga_len - count; + int uf_args_len = ufunc->uf_args.ga_len; int i; char_u *arg; int off = STACK_FRAME_SIZE + (ufunc->uf_va_name != NULL ? 1 : 0); @@ -8210,16 +8228,24 @@ compile_def_function( ufunc->uf_def_arg_idx = ALLOC_CLEAR_MULT(int, count + 1); if (ufunc->uf_def_arg_idx == NULL) goto erret; + SOURCING_LNUM = 0; // line number unknown for (i = 0; i < count; ++i) { garray_T *stack = &cctx.ctx_type_stack; type_T *val_type; int arg_idx = first_def_arg + i; where_T where; + int r; + + // Make sure later arguments are not found. + ufunc->uf_args.ga_len = i; ufunc->uf_def_arg_idx[i] = instr->ga_len; arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i]; - if (compile_expr0(&arg, &cctx) == FAIL) + r = compile_expr0(&arg, &cctx); + + ufunc->uf_args.ga_len = uf_args_len; + if (r == FAIL) goto erret; // If no type specified use the type of the default value. @@ -8418,8 +8444,8 @@ compile_def_function( } } } - p = find_ex_command(&ea, NULL, starts_with_colon ? NULL - : (int (*)(char_u *, size_t, cctx_T *))item_exists, &cctx); + p = find_ex_command(&ea, NULL, starts_with_colon + ? NULL : item_exists, &cctx); if (p == NULL) { diff --git a/src/vim9execute.c b/src/vim9execute.c index 2e7e204e3c..6b8cd075c1 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -30,7 +30,7 @@ typedef struct { int tcd_finally_idx; // instruction of the :finally block or zero int tcd_endtry_idx; // instruction of the :endtry int tcd_caught; // catch block entered - int tcd_cont; // :continue encountered, jump here + int tcd_cont; // :continue encountered, jump here (minus one) int tcd_return; // when TRUE return from end of :finally } trycmd_T; @@ -323,6 +323,8 @@ call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx) else ectx->ec_outer = NULL; + ++ufunc->uf_calls; + // Set execution state to the start of the called function. ectx->ec_dfunc_idx = cdf_idx; ectx->ec_instr = INSTRUCTIONS(dfunc); @@ -556,6 +558,9 @@ func_return(ectx_T *ectx) } } #endif + // TODO: when is it safe to delete the function when it is no longer used? + --dfunc->df_ufunc->uf_calls; + // execution context goes one level up entry = estack_pop(); if (entry != NULL) @@ -807,9 +812,12 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr) // types are correct. for (i = 0; i < argcount; ++i) { - type_T *type = i < ufunc->uf_args.ga_len - ? ufunc->uf_arg_types[i] : ufunc->uf_va_type; + type_T *type = NULL; + if (i < ufunc->uf_args.ga_len) + type = ufunc->uf_arg_types[i]; + else if (ufunc->uf_va_type != NULL) + type = ufunc->uf_va_type->tt_member; if (type != NULL && check_typval_arg_type(type, &argv[i], i + 1) == FAIL) return FAIL; @@ -982,8 +990,9 @@ allocate_if_null(typval_T *tv) } /* - * Return the character "str[index]" where "index" is the character index. If - * "index" is out of range NULL is returned. + * Return the character "str[index]" where "index" is the character index, + * including composing characters. + * If "index" is out of range NULL is returned. */ char_u * char_from_string(char_u *str, varnumber_T index) @@ -1002,7 +1011,7 @@ char_from_string(char_u *str, varnumber_T index) int clen = 0; for (nbyte = 0; nbyte < slen; ++clen) - nbyte += MB_CPTR2LEN(str + nbyte); + nbyte += mb_ptr2len(str + nbyte); nchar = clen + index; if (nchar < 0) // unlike list: index out of range results in empty string @@ -1010,15 +1019,15 @@ char_from_string(char_u *str, varnumber_T index) } for (nbyte = 0; nchar > 0 && nbyte < slen; --nchar) - nbyte += MB_CPTR2LEN(str + nbyte); + nbyte += mb_ptr2len(str + nbyte); if (nbyte >= slen) return NULL; - return vim_strnsave(str + nbyte, MB_CPTR2LEN(str + nbyte)); + return vim_strnsave(str + nbyte, mb_ptr2len(str + nbyte)); } /* * Get the byte index for character index "idx" in string "str" with length - * "str_len". + * "str_len". Composing characters are included. * If going over the end return "str_len". * If "idx" is negative count from the end, -1 is the last character. * When going over the start return -1. @@ -1033,7 +1042,7 @@ char_idx2byte(char_u *str, size_t str_len, varnumber_T idx) { while (nchar > 0 && nbyte < str_len) { - nbyte += MB_CPTR2LEN(str + nbyte); + nbyte += mb_ptr2len(str + nbyte); --nchar; } } @@ -1053,7 +1062,8 @@ char_idx2byte(char_u *str, size_t str_len, varnumber_T idx) } /* - * Return the slice "str[first:last]" using character indexes. + * Return the slice "str[first : last]" using character indexes. Composing + * characters are included. * "exclusive" is TRUE for slice(). * Return NULL when the result is empty. */ @@ -1076,7 +1086,7 @@ string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive) end_byte = char_idx2byte(str, slen, last); if (!exclusive && end_byte >= 0 && end_byte < (long)slen) // end index is inclusive - end_byte += MB_CPTR2LEN(str + end_byte); + end_byte += mb_ptr2len(str + end_byte); } if (start_byte >= (long)slen || end_byte <= start_byte) @@ -1329,7 +1339,7 @@ call_def_function( ++ectx.ec_stack.ga_len; } if (ufunc->uf_va_name != NULL) - ++ectx.ec_stack.ga_len; + ++ectx.ec_stack.ga_len; // Frame pointer points to just after arguments. ectx.ec_frame_idx = ectx.ec_stack.ga_len; @@ -1402,6 +1412,9 @@ call_def_function( // Do turn errors into exceptions. suppress_errthrow = FALSE; + // Do not delete the function while executing it. + ++ufunc->uf_calls; + // When ":silent!" was used before calling then we still abort the // function. If ":silent!" is used in the function then we don't. emsg_silent_def = emsg_silent; @@ -1762,7 +1775,7 @@ call_def_function( goto failed; SOURCING_LNUM = iptr->isn_lnum; if (eval_variable(name, (int)STRLEN(name), - STACK_TV_BOT(0), NULL, TRUE, FALSE) == FAIL) + STACK_TV_BOT(0), NULL, EVAL_VAR_VERBOSE) == FAIL) goto on_error; ++ectx.ec_stack.ga_len; } @@ -2754,7 +2767,9 @@ call_def_function( { trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - i; - trycmd->tcd_cont = iidx; + // Add one to tcd_cont to be able to jump to + // instruction with index zero. + trycmd->tcd_cont = iidx + 1; iidx = trycmd->tcd_finally_idx == 0 ? trycmd->tcd_endtry_idx : trycmd->tcd_finally_idx; } @@ -2808,7 +2823,7 @@ call_def_function( if (trycmd->tcd_cont != 0) // handling :continue: jump to outer try block or // start of the loop - ectx.ec_iidx = trycmd->tcd_cont; + ectx.ec_iidx = trycmd->tcd_cont - 1; } } break; @@ -3244,8 +3259,9 @@ call_def_function( res = string_slice(tv->vval.v_string, n1, n2, FALSE); else // Index: The resulting variable is a string of a - // single character. If the index is too big or - // negative the result is empty. + // single character (including composing characters). + // If the index is too big or negative the result is + // empty. res = char_from_string(tv->vval.v_string, n2); vim_free(tv->vval.v_string); tv->vval.v_string = res; @@ -3830,6 +3846,9 @@ failed: estack_pop(); current_sctx = save_current_sctx; + // TODO: when is it safe to delete the function if it is no longer used? + --ufunc->uf_calls; + if (*msg_list != NULL && saved_msg_list != NULL) { msglist_T **plist = saved_msg_list; diff --git a/src/vim9script.c b/src/vim9script.c index 5ce7c3bdb7..05977b641c 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -75,7 +75,7 @@ ex_vim9script(exarg_T *eap UNUSED) if (STRCMP(p_cpo, CPO_VIM) != 0) { si->sn_save_cpo = vim_strsave(p_cpo); - set_option_value((char_u *)"cpo", 0L, (char_u *)CPO_VIM, 0); + set_option_value((char_u *)"cpo", 0L, (char_u *)CPO_VIM, OPT_NO_REDRAW); } #else // No check for this being the first command, it doesn't matter. @@ -102,6 +102,7 @@ not_in_vim9(exarg_T *eap) case CMD_append: case CMD_change: case CMD_insert: + case CMD_open: case CMD_t: case CMD_xit: semsg(_(e_command_not_supported_in_vim9_script_missing_var_str), eap->cmd); @@ -257,7 +258,8 @@ find_exported( char_u *name, ufunc_T **ufunc, type_T **type, - cctx_T *cctx) + cctx_T *cctx, + int verbose) { int idx = -1; svar_T *sv; @@ -271,7 +273,8 @@ find_exported( sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; if (!sv->sv_export) { - semsg(_(e_item_not_exported_in_script_str), name); + if (verbose) + semsg(_(e_item_not_exported_in_script_str), name); return -1; } *type = sv->sv_type; @@ -301,7 +304,8 @@ find_exported( if (*ufunc == NULL) { - semsg(_(e_item_not_found_in_script_str), name); + if (verbose) + semsg(_(e_item_not_found_in_script_str), name); return -1; } } @@ -532,7 +536,7 @@ handle_import( ufunc_T *ufunc = NULL; type_T *type; - idx = find_exported(sid, name, &ufunc, &type, cctx); + idx = find_exported(sid, name, &ufunc, &type, cctx, TRUE); if (idx < 0 && ufunc == NULL) goto erret; diff --git a/src/vim9type.c b/src/vim9type.c index d1ada8e96b..9eda30d6e6 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -919,6 +919,8 @@ equal_type(type_T *type1, type_T *type2) { int i; + if (type1 == NULL || type2 == NULL) + return FALSE; if (type1->tt_type != type2->tt_type) return FALSE; switch (type1->tt_type) @@ -969,12 +971,12 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap) // If either is VAR_UNKNOWN use the other type. An empty list/dict has no // specific type. - if (type1->tt_type == VAR_UNKNOWN) + if (type1 == NULL || type1->tt_type == VAR_UNKNOWN) { *dest = type2; return; } - if (type2->tt_type == VAR_UNKNOWN) + if (type2 == NULL || type2->tt_type == VAR_UNKNOWN) { *dest = type1; return; diff --git a/src/window.c b/src/window.c index 270399c1da..3e0aa23a43 100644 --- a/src/window.c +++ b/src/window.c @@ -5865,8 +5865,8 @@ win_setminheight(void) // loop until there is a 'winminheight' that is possible while (p_wmh > 0) { - room = Rows - p_ch - tabline_height(); - needed = frame_minheight(topframe, NULL); + room = Rows - p_ch; + needed = min_rows() - 1; // 1 was added for the cmdline if (room >= needed) break; --p_wmh;