From 48e11c10548782f573411b6302f77adb69c40401 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 11 Jan 2021 18:47:00 +0100 Subject: [PATCH 01/40] patch 8.2.2328: some test files may not be deleted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Some test files may not be deleted. Solution: Add a delete() call, correct name. (Dominique Pellé, closes #7654) --- src/testdir/test_clientserver.vim | 1 + src/testdir/test_vim9_script.vim | 4 ++-- src/version.c | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_clientserver.vim b/src/testdir/test_clientserver.vim index f838e221ab..6a73d93e5b 100644 --- a/src/testdir/test_clientserver.vim +++ b/src/testdir/test_clientserver.vim @@ -81,6 +81,7 @@ func Test_client_server() call writefile(['one', 'two'], 'Xclientfile') call system(cmd) call WaitForAssert({-> assert_equal('two', remote_expr(name, "getline(2)", "", 2))}) + call delete('Xclientfile') " Expression evaluated locally. if v:servername == '' diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index d9aa71e268..1066a1d43e 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -1238,7 +1238,7 @@ def Test_vim9script_reload_noclear() assert_equal(3, g:loadCount) assert_equal(['init', 'yes', 'again', 'once', 'thexport'], g:Values()) - delete('Xreloaded') + delete('XReloaded') delete('XExportReload') delfunc g:Values unlet g:loadCount @@ -2966,7 +2966,7 @@ def Test_vim9_autoload_error() invalid endfu try - invalid + alsoinvalid catch /wontmatch/ endtry END diff --git a/src/version.c b/src/version.c index e64265fb27..f56da436b4 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2328, /**/ 2327, /**/ From 82be4849eed0b8fbee45bc8da99b685ec89af59a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 11 Jan 2021 19:40:15 +0100 Subject: [PATCH 02/40] Update runtime files. --- .github/CODEOWNERS | 1 + runtime/doc/editing.txt | 4 +- runtime/doc/options.txt | 4 +- runtime/doc/os_vms.txt | 8 +- runtime/doc/pattern.txt | 7 +- runtime/doc/tags | 11 ++ runtime/doc/terminal.txt | 10 +- runtime/doc/todo.txt | 107 +++++++----- runtime/doc/usr_03.txt | 2 +- runtime/doc/usr_41.txt | 8 +- runtime/doc/vim9.txt | 67 ++++++-- runtime/ftplugin/erlang.vim | 114 ++++++------ runtime/ftplugin/fstab.vim | 4 +- runtime/ftplugin/swift.vim | 11 +- runtime/ftplugin/vim.vim | 19 +- runtime/indent/systemverilog.vim | 2 +- runtime/indent/testdir/vim.in | 19 ++ runtime/indent/testdir/vim.ok | 19 ++ runtime/indent/vim.vim | 46 ++++- runtime/lang/menu_tr.cp1254.vim | 2 +- runtime/lang/menu_tr.iso_8859-9.vim | 2 +- .../dist/opt/termdebug/plugin/termdebug.vim | 162 +++++++++++++++++- runtime/syntax/abap.vim | 4 +- runtime/syntax/prolog.vim | 10 +- 24 files changed, 481 insertions(+), 162 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7401f419f8..970cc942dc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -164,6 +164,7 @@ runtime/syntax/nsis.vim @k-takata runtime/syntax/pdf.vim @tpope runtime/syntax/php.vim @TysonAndre runtime/syntax/privoxy.vim @dkearns +runtime/syntax/prolog.vim @XVilka runtime/syntax/rc.vim @chrisbra runtime/syntax/rpcgen.vim @cecamp runtime/syntax/ruby.vim @dkearns diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 7f30e54ae3..4c4de79ee3 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1,4 +1,4 @@ -*editing.txt* For Vim version 8.2. Last change: 2020 Dec 19 +*editing.txt* For Vim version 8.2. Last change: 2021 Jan 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1014,7 +1014,7 @@ to write anyway add a '!' to the command. *write-permissions* When writing a new file the permissions are read-write. For unix the mask is -0666 with additionally umask applied. When writing a file that was read Vim +0o666 with additionally umask applied. When writing a file that was read Vim will preserve the permissions, but clear the s-bit. *write-readonly* diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4d26df86ef..2963193a00 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 8.2. Last change: 2020 Dec 21 +*options.txt* For Vim version 8.2. Last change: 2021 Jan 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -438,7 +438,7 @@ the system, mostly it is something like 256 or 1024 characters. CTRL-? CTRL-H not CTRL-? CTRL-? - (CTRL-? is 0177 octal, 0x7f hex) + (CTRL-? is 0o177 octal, 0x7f hex) If your delete key terminal code is wrong, but the code for backspace is alright, you can put this in diff --git a/runtime/doc/os_vms.txt b/runtime/doc/os_vms.txt index f5c48590e5..6eb2993bc7 100644 --- a/runtime/doc/os_vms.txt +++ b/runtime/doc/os_vms.txt @@ -1,4 +1,4 @@ -*os_vms.txt* For Vim version 8.2. Last change: 2020 Dec 30 +*os_vms.txt* For Vim version 8.2. Last change: 2021 Jan 04 VIM REFERENCE MANUAL @@ -767,8 +767,10 @@ GNU_TOOLS.ZIP package downloadable from http://www.polarhome.com/vim/ Version 8.2 - make all changes needed for clean compile build of v8.2 on VMS on all platforms -- test on VSI OpenVMS platforms -- added XPM support - Motif GUI with toolbar on all platforms +- fix the call mkdir bug (vicente_polo@yahoo.es) +- test on VSI OpenVMS Alpha and Itanium platforms +- added LUA support +- added XPM support - Motif GUI with toolbar on all platforms - XPM v3.4.11 libraries for IA64, AXP and VAX are added - start integrating the new test scripts diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index 177a652b76..82b4418be6 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -1,4 +1,4 @@ -*pattern.txt* For Vim version 8.2. Last change: 2020 Dec 25 +*pattern.txt* For Vim version 8.2. Last change: 2021 Jan 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1179,7 +1179,7 @@ x A single character, with no special meaning, matches itself \b \n line break, see above |/[\n]| \d123 decimal number of character - \o40 octal number of character up to 0377 + \o40 octal number of character up to 0o377 \x20 hexadecimal number of character up to 0xff \u20AC hex. number of multibyte character up to 0xffff \U1234 hex. number of multibyte character up to 0xffffffff @@ -1217,7 +1217,8 @@ x A single character, with no special meaning, matches itself \%d123 Matches the character specified with a decimal number. Must be followed by a non-digit. \%o40 Matches the character specified with an octal number up to 0377. - Numbers below 040 must be followed by a non-octal digit or a non-digit. + Numbers below 0o40 must be followed by a non-octal digit or a + non-digit. \%x2a Matches the character specified with up to two hexadecimal characters. \%u20AC Matches the character specified with up to four hexadecimal characters. diff --git a/runtime/doc/tags b/runtime/doc/tags index a32731db86..1b36eb2984 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -1783,6 +1783,8 @@ $VIM_POSIX vi_diff.txt /*$VIM_POSIX* 09.2 usr_09.txt /*09.2* 09.3 usr_09.txt /*09.3* 09.4 usr_09.txt /*09.4* +0o eval.txt /*0o* +0x eval.txt /*0x* 10.1 usr_10.txt /*10.1* 10.2 usr_10.txt /*10.2* 10.3 usr_10.txt /*10.3* @@ -1994,6 +1996,7 @@ $VIM_POSIX vi_diff.txt /*$VIM_POSIX* :AdaTagFile ft_ada.txt /*:AdaTagFile* :AdaTypes ft_ada.txt /*:AdaTypes* :Arguments terminal.txt /*:Arguments* +:Asm terminal.txt /*:Asm* :Break terminal.txt /*:Break* :Cfilter quickfix.txt /*:Cfilter* :Clear terminal.txt /*:Clear* @@ -5756,6 +5759,7 @@ characterwise motion.txt /*characterwise* characterwise-register change.txt /*characterwise-register* characterwise-visual visual.txt /*characterwise-visual* charclass() eval.txt /*charclass()* +charcol() eval.txt /*charcol()* charconvert_from-variable eval.txt /*charconvert_from-variable* charconvert_to-variable eval.txt /*charconvert_to-variable* charidx() eval.txt /*charidx()* @@ -6951,6 +6955,7 @@ getbufvar() eval.txt /*getbufvar()* getchangelist() eval.txt /*getchangelist()* getchar() eval.txt /*getchar()* getcharmod() eval.txt /*getcharmod()* +getcharpos() eval.txt /*getcharpos()* getcharsearch() eval.txt /*getcharsearch()* getcmdline() eval.txt /*getcmdline()* getcmdpos() eval.txt /*getcmdpos()* @@ -6958,6 +6963,7 @@ getcmdtype() eval.txt /*getcmdtype()* getcmdwintype() eval.txt /*getcmdwintype()* getcompletion() eval.txt /*getcompletion()* getcurpos() eval.txt /*getcurpos()* +getcursorcharpos() eval.txt /*getcursorcharpos()* getcwd() eval.txt /*getcwd()* getenv() eval.txt /*getenv()* getfontname() eval.txt /*getfontname()* @@ -8260,6 +8266,7 @@ notation intro.txt /*notation* notepad gui_w32.txt /*notepad* nr2char() eval.txt /*nr2char()* nroff.vim syntax.txt /*nroff.vim* +null vim9.txt /*null* null-variable eval.txt /*null-variable* number_relativenumber options.txt /*number_relativenumber* numbered-function eval.txt /*numbered-function* @@ -8871,8 +8878,10 @@ set-spc-auto spell.txt /*set-spc-auto* setbufline() eval.txt /*setbufline()* setbufvar() eval.txt /*setbufvar()* setcellwidths() eval.txt /*setcellwidths()* +setcharpos() eval.txt /*setcharpos()* setcharsearch() eval.txt /*setcharsearch()* setcmdpos() eval.txt /*setcmdpos()* +setcursorcharpos() eval.txt /*setcursorcharpos()* setenv() eval.txt /*setenv()* setfperm() eval.txt /*setfperm()* setline() eval.txt /*setline()* @@ -9553,6 +9562,7 @@ termdebug-prompt terminal.txt /*termdebug-prompt* termdebug-starting terminal.txt /*termdebug-starting* termdebug-stepping terminal.txt /*termdebug-stepping* termdebug-variables terminal.txt /*termdebug-variables* +termdebug_disasm_window terminal.txt /*termdebug_disasm_window* termdebug_map_K terminal.txt /*termdebug_map_K* termdebug_popup terminal.txt /*termdebug_popup* termdebug_shortcuts terminal.txt /*termdebug_shortcuts* @@ -10086,6 +10096,7 @@ vim9-reload vim9.txt /*vim9-reload* vim9-scopes vim9.txt /*vim9-scopes* vim9-script-intro usr_46.txt /*vim9-script-intro* vim9-types vim9.txt /*vim9-types* +vim9-user-command vim9.txt /*vim9-user-command* vim9.txt vim9.txt /*vim9.txt* vim9script vim9.txt /*vim9script* vim: options.txt /*vim:* diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index 195122910a..5885cb1976 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -1,4 +1,4 @@ -*terminal.txt* For Vim version 8.2. Last change: 2020 Dec 28 +*terminal.txt* For Vim version 8.2. Last change: 2021 Jan 04 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1330,6 +1330,8 @@ Other commands ~ *:Program* jump to the window with the running program *:Source* jump to the window with the source code, create it if there isn't one + *:Asm* jump to the window with the disassembly, create it if there + isn't one Prompt mode ~ @@ -1352,6 +1354,12 @@ Prompt mode can be used even when the |+terminal| feature is present with: > The K key is normally mapped to :Evaluate. If you do not want this use: > let g:termdebug_map_K = 0 +< + *termdebug_disasm_window* +If you want the Asm window shown by default, set this to 1. Setting to +any value greater than 1 will set the Asm window height to that value: > + let g:termdebug_disasm_window = 15 +< Communication ~ *termdebug-communication* diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index 07779b470e..6e5fe4cc09 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 Jan 02 +*todo.txt* For Vim version 8.2. Last change: 2021 Jan 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -38,18 +38,29 @@ browser use: https://github.com/vim/vim/issues/1234 *known-bugs* -------------------- Known bugs and current work ----------------------- -Reload: How to make sure type of script function hasn't changed? - -Valgrind: test_vim9_cmd: uses uninitialized var +no error for: + echo extend([0], ['a', true]) +like it does for: + echo extend([0], ['a']) +At script level this does not give an error: + echo map([0], (_, v) => []) +Or: + var l: list = [0] + echo map(l, (_, v) => []) Vim9 - Make everything work: +- Expand `=expr` in :next, :argedit, :argadd, :argdelete, :drop +- Expand `=expr` in :vimgrep, :vimgrepadd, :lvimgrep, :lvimgrepadd +- Expand `=expr` in :mkspell +- Unlet with range: "unlet list[a : b]" - Implement "export {one, two three}". -- Use "null" for v:null, like true and false? #7495 - 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: let p = function('NoSuchFunc') +- 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 the function type. def Filter(x: string, Cond: func(string): bool) @@ -63,25 +74,18 @@ Vim9 - Make everything work: statement statement } -- implement { cmd } compiled -- implement { cmd } at script level -- Expand `=expr` in :next, :argedit, :argadd, :argdelete, :drop -- Expand `=expr` in :vimgrep, :vimgrepadd, :lvimgrep, :lvimgrepadd -- Expand `=expr` in :mkspell - Does this work already: can use func as reference: def SomeFunc() ... map(list, SomeFunc) - For builtin functions using tv_get_string*() use check_for_string() to be more strict about the argument type. +- Possible memory leaks in test_vim9_func - Implement :lockvar and :unlockvar. How about local variables? Perhaps only allow this for global variables. Use :final or :const otherwise. - Allow function names that will be script-local to start with lower case letter? Or also require names with s: prefix to use upper case? Also apply this function references "var ref = SomeFunc" - Support passing v:none to use the default argument value. (#6504) -- Make map() give an error if the resulting type of the first argument is - wrong. Only works if the type is known? Is this slow (need to go over all - items)? - Run the same tests in :def and Vim9 script, like in Test_expr7_not() - Check many more builtin function arguments at compile time. - make 0 == 'string' fail on the script level, like inside :def. @@ -105,57 +109,59 @@ Vim9 - Make everything work: - 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. -- Disallow unlet for local/script/imported vars - Make "++nr" work. "++g:count" doesn't work, thinks it is a range. -- Make closures work: - - Create closure in a loop. Need to make a list of them. - - nested closure only uses one context, not all (#7150) +- Reload: How to make sure type of script function hasn't changed? - expandcmd() with `=expr` in filename uses legacy expression. - eval_expr() in ex_cexpr() - eval_expr() call in dbg_parsearg() and debuggy_find() -Improve error checking: -- "echo Func()" is an error if Func() does not return anything. -Before launch: +- compile "skip" argument of searchpair() +- compile "expr" and "call" expression of a channel in channel_exe_cmd()? +- give an error for "echo Func()" if Func() does not return anything. + +Once Vim9 is stable: +- Change the help to prefer Vim9 syntax where appropriate +- Use Vim9 for runtime files. + PR #7497 for autoload/ccomplete.vim - Add all the error numbers in a good place in documentation. - In the generic eval docs, point out the Vim9 syntax where it differs. -Also: -- For range: make table of first ASCII character with flag to quickly check if - it can be a Vim9 command. E.g. "+" can, but "." can't. + +Also for Vim9: - better implementation for partial and tests for that. - Make "g:imported = Export.exported" work in Vim9 script. - Make Foo.Bar() work to call the dict function. (#5676) - Error in any command in "vim9script" aborts sourcing. - Find a way to test expressions in legacy and Vim9 script without duplication -- Test each level of expressions properly, with type checking - Test try/catch and throw better, also nested. - Test return inside try/finally jumps to finally and then returns. + Test that return inside try/finally jumps to finally and then returns. - Test that a function defined inside a :def function is local to that function, g: functions can be defined and script-local functions cannot be defined. -- implement :type -- import type declaration? +- compile options that are an expression, e.g. "expr:" in 'spellsuggest', + 'foldexpr', 'foldtext', 'printexpr', 'diffexpr', 'patchexpr', 'charconvert', + 'balloonexpr', 'includeexpr', 'indentexpr', 'formatexpr'. + Give an error if compilation fails. (#7625) + Use the location where the option was set for deciding whether it's to be + evaluated in Vim9 script context. +- implement :type; import type declaration. +- implement enum; import enum. - Future work: See |vim9-classes| -- implement enum -- Make accessing varargs faster: arg[expr] - EVAL expr - LOADVARARG (varargs idx) + Define the keywords and commands to make sure it will be backwards + compatible. - Make debugging work - at least per function. Need to recompile a function to step through it line-by-line? Evaluate the stack and variables on the stack? - Make profiling work - Add ISN_PROFILE instructions after every line? - List commands when 'verbose' is set or :verbose is used. -Once Vim9 is stable: -- Change the help to prefer Vim9 syntax where appropriate -- Use Vim9 for runtime files. - PR #7497 for autoload/ccomplete.vim -Further improvements: -- compile options that are an expression, e.g. "expr:" in 'spellsuggest', - 'foldexpr', 'foldtext', 'printexpr', 'diffexpr', 'patchexpr', 'charconvert', - 'balloonexpr', 'includeexpr', 'indentexpr', 'formatexpr'. + +Further Vim9 improvements, possibly after launch: +- For range: make table of first ASCII character with flag to quickly check if + it can be a Vim9 command. E.g. "+" can, but "." can't. - compile get_lambda_tv() in popup_add_timeout() - inline call to map() and filter() -- compile "skip" argument of searchpair() -- compile "expr" and "call" expression of a channel in channel_exe_cmd()? +- Make accessing varargs faster: arg[expr] + EVAL expr + LOADVARARG (varargs idx) + Popup windows: - Add a flag to make a popup window focusable? @@ -308,6 +314,18 @@ Any way to convert "$" back by using a special value? (#6901) Can we detect true color support? https://gist.github.com/XVilka/8346728 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) + +Changing a capturing group to non-capturing changes the result: #7607 + :echo matchstr('aaa bbb', '\(.\{-1,}\>\)\|.*') + aaa + :echo matchstr('aaa bbb', '\%(.\{-1,}\>\)\|.*') + aaa bbb +Should also work without any group: + :echo matchstr('aaa bbb', '.\{-1,}\>\|.*') + aaa bbb (should be aaa) + Check out PR #543 (Roland Puntaier). Patch for multibyte characters in langmap and applying a mapping on them. (Christian Brabandt, 2015 Jun 12, update July 25) @@ -393,9 +411,6 @@ corruption. (#6631) When 'lazyredraw' is set sometimes the title is not updated. (Jason Franklin, 2020 Feb 3) Looks like a race condition. -Regexp to search for duplicate lines does not work correctly: -/\(^.*\n\)\1 (Chris Morgan, #6239) - With bash ":make" does not set v:shell_error. Possible solution: set 'shellpipe' to "2>&1| tee %s; exit ${PIPESTATUS[0]}" #5994 @@ -1152,7 +1167,7 @@ Overlong utf-8 sequence is displayed wrong. (Harm te Hennepe, 2017 Sep 14, #2089) Patch with possible solution by Björn Linse. The change list index is local to a buffer, but it doesn't make sense using it -for another buffer. (lacygolil) Copy w_changelistidx to wininfo_S and back. +for another buffer. (lacygoill) Copy w_changelistidx to wininfo_S and back. X11: Putting more than about 262040 characters of text on the clipboard and pasting it in another Vim doesn't work. (Dominique Pelle, 2008 Aug 21-23) @@ -5474,7 +5489,7 @@ Mappings and Abbreviations: not the . 8 Give a warning when using CTRL-C in the lhs of a mapping. It will never (?) work. -7 Add <0x8f> (hex), <033> (octal) and <123> (decimal) to <> notation? +7 Add <0x8f> (hex), <0o33> (octal) and <123> (decimal) to <> notation? 7 When someone tries to unmap with a trailing space, and it fails, try unmapping without the trailing space. Helps for ":unmap xx | unmap yy". 6 Context-sensitive abbreviations: Specify syntax group(s) in which the diff --git a/runtime/doc/usr_03.txt b/runtime/doc/usr_03.txt index c3bf2d312e..d30bddfc62 100644 --- a/runtime/doc/usr_03.txt +++ b/runtime/doc/usr_03.txt @@ -50,7 +50,7 @@ which moves to the previous end of a word: This is a line with example text ~ <----<----x---->------------> - 2ge ge e we + 2ge ge e 2e If you are at the last word of a line, the "w" command will take you to the first word in the next line. Thus you can use this to move through a diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index da095e13f4..7d4e3a2b09 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1,4 +1,4 @@ -*usr_41.txt* For Vim version 8.2. Last change: 2020 Dec 19 +*usr_41.txt* For Vim version 8.2. Last change: 2021 Jan 08 VIM USER MANUAL - by Bram Moolenaar @@ -122,14 +122,14 @@ starts with a zero. "017" is decimal 15. A binary number starts with "0b" or decimal number, it will be interpreted as an octal number! The ":echo" command always prints decimal numbers. Example: > - :echo 0x7f 036 + :echo 0x7f 0o36 < 127 30 ~ A number is made negative with a minus sign. This also works for hexadecimal, octal and binary numbers. A minus sign is also used for subtraction. Compare this with the previous example: > - :echo 0x7f -036 + :echo 0x7f -0o36 < 97 ~ White space in an expression is ignored. However, it's recommended to use it @@ -137,7 +137,7 @@ for separating items, to make the expression easier to read. For example, to avoid the confusion with a negative number above, put a space between the minus sign and the following number: > - :echo 0x7f - 036 + :echo 0x7f - 0o36 ============================================================================== *41.2* Variables diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index b30db0b869..a7b91db0a4 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 Jan 03 +*vim9.txt* For Vim version 8.2. Last change: 2021 Jan 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52,8 +52,9 @@ The Vim9 script syntax and semantics are used in: - a script file where the first command is `vim9script` - an autocommand defined in the context of the above -When using `:function` in a Vim9 script file the legacy syntax is used. -However, this can be confusing and is therefore discouraged. +When using `:function` in a Vim9 script file the legacy syntax is used, with +the highest |scriptversion|. However, this can be confusing and is therefore +discouraged. Vim9 script and legacy Vim script can be mixed. There is no requirement to rewrite old scripts, they keep working as before. You may want to use a few @@ -70,28 +71,29 @@ Overview ~ Brief summary of the differences you will most often encounter when using Vim9 script and `:def` functions; details are below: - Comments start with #, not ": > - echo "hello" # comment + echo "hello" # comment - Using a backslash for line continuation is hardly ever needed: > - echo "hello " + echo "hello " .. yourName .. ", how are you?" - White space is required in many places. - Assign values without `:let`, declare variables with `:var`: > - var count = 0 + var count = 0 count += 3 - Constants can be declared with `:final` and `:const`: > - final matches = [] # add matches + final matches = [] # add matches const names = ['Betty', 'Peter'] # cannot be changed - `:final` cannot be used as an abbreviation of `:finally`. - Variables and functions are script-local by default. - Functions are declared with argument types and return type: > def CallMe(count: number, message: string): bool - Call functions without `:call`: > - writefile(['done'], 'file.txt') + writefile(['done'], 'file.txt') - You cannot use `:xit`, `:t`, `:append`, `:change`, `:insert` or curly-braces names. - A range before a command must be prefixed with a colon: > - :%s/this/that + :%s/this/that +- Unless mentioned specifically, the highest |scriptversion| is used. Comments starting with # ~ @@ -315,7 +317,7 @@ The constant only applies to the value itself, not what it refers to. > NAMES[0] = ["Jack"] # Error! NAMES[0][0] = "Jack" # Error! NAMES[1] = ["Emma"] # Error! - Names[1][0] = "Emma" # OK, now females[0] == "Emma" + NAMES[1][0] = "Emma" # OK, now females[0] == "Emma" < *E1092* Declaring more than one variable at a time, using the unpack notation, is @@ -432,7 +434,7 @@ possible just before or after the operator. For example: > .. middle .. end var total = start + - end - + end - correction var result = positive ? PosFunc(arg) @@ -629,9 +631,9 @@ one: > When using "!" for inverting, there is no error for using any type and the result is a boolean. "!!" can be used to turn any value into boolean: > - !'yes' == false + !'yes' == false !![] == false - !![1, 2, 3] == true + !![1, 2, 3] == true When using "`.."` for string concatenation arguments of simple types are always converted to string: > @@ -651,6 +653,14 @@ byte indexes. 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 'á'. +A negative index is counting from the end, "[-1]" is the last character. +If the index is out of range then an empty string results. + +In legacy script "++var" and "--var" would be silently accepted and have no +effect. This is an error in Vim9 script. + +Numbers starting with zero are not considered to be octal, only numbers +starting with "0o" are octal: "0o744". |scriptversion-4| What to watch out for ~ @@ -689,7 +699,7 @@ Vim9 functions are compiled as a whole: > if !has('feature') return endif - use-feature # May give compilation error + use-feature # May give a compilation error enddef For a workaround, split it in two functions: > func Maybe() @@ -709,6 +719,25 @@ evaluates to false: > use-feature endif enddef +< *vim9-user-command* +Another side effect of compiling a function is that the precense of a user +command is checked at compile time. If the user command is defined later an +error will result. This works: > + command -nargs=1 MyCommand echom + def Works() + MyCommand 123 + enddef +This will give an error for "MyCommand" not being defined: > + def Works() + command -nargs=1 MyCommand echom + MyCommand 123 + enddef +A workaround is to invoke the command indirectly with `:execute`: > + def Works() + command -nargs=1 MyCommand echom + execute 'MyCommand 123' + enddef + Note that for unrecognized commands there is no check for "|" and a following command. This will give an error for missing `endif`: > def Maybe() @@ -946,6 +975,12 @@ an error, thus breaking backwards compatibility. For example: - Using a string value when setting a number options. - Using a number where a string is expected. *E1024* +One consequence is that the item type of a list or dict given to map() must +not change. This will give an error in compiled code: > + map([1, 2, 3], (i, v) => 'item ' .. i) + E1012: Type mismatch; expected list but got list +Instead use |mapnew()|. + ============================================================================== 5. Namespace, Import and Export @@ -1055,14 +1090,14 @@ actually needed. A recommended mechanism: 1. In the plugin define user commands, functions and/or mappings that refer to an autoload script. > - command -nargs=1 SearchForStuff call searchfor#Stuff() + command -nargs=1 SearchForStuff call searchfor#Stuff() < This goes in .../plugin/anyname.vim. "anyname.vim" can be freely chosen. 2. In the autoload script do the actual work. You can import items from other files to split up functionality in appropriate pieces. > vim9script - import FilterFunc from "../import/someother.vim" + import FilterFunc from "../import/someother.vim" def searchfor#Stuff(arg: string) var filtered = FilterFunc(arg) ... diff --git a/runtime/ftplugin/erlang.vim b/runtime/ftplugin/erlang.vim index ece11d7249..c775247f51 100644 --- a/runtime/ftplugin/erlang.vim +++ b/runtime/ftplugin/erlang.vim @@ -1,87 +1,83 @@ " Vim ftplugin file -" Language: Erlang +" Language: Erlang (http://www.erlang.org) +" Maintainer: Csaba Hoch " Author: Oscar Hellström " Contributors: Ricardo Catalinas Jiménez " Eduardo Lopez (http://github.com/tapichu) +" Arvid Bjurklint (http://github.com/slarwise) +" Last Update: 2021-Jan-08 " License: Vim license -" Version: 2012/01/25 +" URL: https://github.com/vim-erlang/vim-erlang-runtime if exists('b:did_ftplugin') - finish -else - let b:did_ftplugin = 1 -endif - -if exists('s:did_function_definitions') - call s:SetErlangOptions() - finish -else - let s:did_function_definitions = 1 + finish endif +let b:did_ftplugin = 1 let s:cpo_save = &cpo set cpo&vim -if !exists('g:erlang_keywordprg') - let g:erlang_keywordprg = 'erl -man' +let &l:keywordprg = get(g:, 'erlang_keywordprg', 'erl -man') + +if get(g:, 'erlang_folding', 0) + setlocal foldmethod=expr + setlocal foldexpr=GetErlangFold(v:lnum) + setlocal foldtext=ErlangFoldText() endif -if !exists('g:erlang_folding') - let g:erlang_folding = 0 -endif +setlocal comments=:%%%,:%%,:% +setlocal commentstring=%%s + +setlocal formatoptions+=ro + +setlocal suffixesadd=.erl,.hrl + +let &l:include = '^\s*-\%(include\|include_lib\)\s*("\zs\f*\ze")' +let &l:define = '^\s*-\%(define\|record\|type\|opaque\)' let s:erlang_fun_begin = '^\a\w*(.*$' let s:erlang_fun_end = '^[^%]*\.\s*\(%.*\)\?$' -function s:SetErlangOptions() - if g:erlang_folding - setlocal foldmethod=expr - setlocal foldexpr=GetErlangFold(v:lnum) - setlocal foldtext=ErlangFoldText() - endif +if !exists('*GetErlangFold') + function GetErlangFold(lnum) + let lnum = a:lnum + let line = getline(lnum) - setlocal comments=:%%%,:%%,:% - setlocal commentstring=%%s + if line =~ s:erlang_fun_end + return '<1' + endif - setlocal formatoptions+=ro - let &l:keywordprg = g:erlang_keywordprg -endfunction + if line =~ s:erlang_fun_begin && foldlevel(lnum - 1) == 1 + return '1' + endif -function GetErlangFold(lnum) - let lnum = a:lnum - let line = getline(lnum) + if line =~ s:erlang_fun_begin + return '>1' + endif - if line =~ s:erlang_fun_end - return '<1' - endif + return '=' + endfunction +endif - if line =~ s:erlang_fun_begin && foldlevel(lnum - 1) == 1 - return '1' - endif +if !exists('*ErlangFoldText') + function ErlangFoldText() + let line = getline(v:foldstart) + let foldlen = v:foldend - v:foldstart + 1 + let lines = ' ' . foldlen . ' lines: ' . substitute(line, "[\ \t]*", '', '') + if foldlen < 10 + let lines = ' ' . lines + endif + let retval = '+' . v:folddashes . lines - if line =~ s:erlang_fun_begin - return '>1' - endif + return retval + endfunction +endif - return '=' -endfunction - -function ErlangFoldText() - let line = getline(v:foldstart) - let foldlen = v:foldend - v:foldstart + 1 - let lines = ' ' . foldlen . ' lines: ' . substitute(line, "[\ \t]*", '', '') - if foldlen < 10 - let lines = ' ' . lines - endif - let retval = '+' . v:folddashes . lines - - return retval -endfunction - -call s:SetErlangOptions() - -let b:undo_ftplugin = "setlocal foldmethod< foldexpr< foldtext<" - \ . " comments< commentstring< formatoptions<" +let b:undo_ftplugin = "setlocal keywordprg< foldmethod< foldexpr< foldtext<" + \ . " comments< commentstring< formatoptions< suffixesadd< include<" + \ . " define<" let &cpo = s:cpo_save unlet s:cpo_save + +" vim: sw=2 et diff --git a/runtime/ftplugin/fstab.vim b/runtime/ftplugin/fstab.vim index 3e7af9fb30..99805322cd 100644 --- a/runtime/ftplugin/fstab.vim +++ b/runtime/ftplugin/fstab.vim @@ -2,7 +2,7 @@ " Language: fstab file " Maintainer: Radu Dineiu " URL: https://raw.github.com/rid9/vim-fstab/master/ftplugin/fstab.vim -" Last Change: 2020 Dec 29 +" Last Change: 2021 Jan 02 " Version: 1.0 " " Credits: @@ -16,4 +16,4 @@ let b:did_ftplugin = 1 setlocal commentstring=#%s let b:undo_ftplugin = "setlocal commentstring<" -" vim: ts=8 ft=vim \ No newline at end of file +" vim: ts=8 ft=vim diff --git a/runtime/ftplugin/swift.vim b/runtime/ftplugin/swift.vim index 55678f8bfc..a86b782c22 100644 --- a/runtime/ftplugin/swift.vim +++ b/runtime/ftplugin/swift.vim @@ -6,7 +6,16 @@ " See https://swift.org/LICENSE.txt for license information " See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors " -" Vim maintainer: Emir SARI +" Vim maintainer: Emir SARI +" Last Change: 2021 Jan 08 + +" Only do this when not done yet for this buffer +if exists("b:did_ftplugin") + finish +endif + +let b:did_ftplugin = 1 +let b:undo_ftplugin = "setlocal comments< expandtab< tabstop< shiftwidth< smartindent<" setlocal comments=s1:/*,mb:*,ex:*/,:///,:// setlocal expandtab diff --git a/runtime/ftplugin/vim.vim b/runtime/ftplugin/vim.vim index b87e144220..3307de5410 100644 --- a/runtime/ftplugin/vim.vim +++ b/runtime/ftplugin/vim.vim @@ -1,7 +1,7 @@ " Vim filetype plugin " Language: Vim " Maintainer: Bram Moolenaar -" Last Change: 2020 Aug 14 +" Last Change: 2021 Jan 05 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -48,18 +48,23 @@ setlocal isk+=# " Use :help to lookup the keyword under the cursor with K. setlocal keywordprg=:help -" Set 'comments' to format dashed lists in comments -" Avoid that #{} starts a comment. -setlocal com=sO:\"\ -,mO:\"\ \ ,sO:#\ -,mO:#\ \ ,eO:##,:\",b:# +if "\n" .. getline(1, 10)->join("\n") =~# '\n\s*vim9\%[script]\>' + " Set 'comments' to format dashed lists in comments + setlocal com=sO:#\ -,mO:#\ \ ,eO:##,:# + " Comments start with a double quote in a legacy script; + " with # in a Vim9 script + setlocal commentstring=\"%s +else + setlocal com=sO:\"\ -,mO:\"\ \ ,:\" + setlocal commentstring=#%s +endif + " Format comments to be up to 78 characters long if &tw == 0 setlocal tw=78 endif -" Comments start with a double quote; in Vim9 script # would also work -setlocal commentstring=\"%s - if !exists("no_plugin_maps") && !exists("no_vim_maps") let b:did_add_maps = 1 diff --git a/runtime/indent/systemverilog.vim b/runtime/indent/systemverilog.vim index 68487f84ba..590fd4d998 100644 --- a/runtime/indent/systemverilog.vim +++ b/runtime/indent/systemverilog.vim @@ -227,4 +227,4 @@ endfunction let &cpo = s:cpo_save unlet s:cpo_save -" vim:sw=2 \ No newline at end of file +" vim:sw=2 diff --git a/runtime/indent/testdir/vim.in b/runtime/indent/testdir/vim.in index 235f31d061..47e692975f 100644 --- a/runtime/indent/testdir/vim.in +++ b/runtime/indent/testdir/vim.in @@ -21,8 +21,27 @@ let cmd = " END_INDENT +" START_INDENT +" INDENT_EXE let g:vim_indent_cont = 5 + +let list = [ +\ 'one', +\ 'two'] + +" END_INDENT + " START_INDENT " INDENT_EXE unlet g:vim_indent_cont + +let list = [ +'one', +'two', +] +echo + +" END_INDENT + +" START_INDENT " INDENT_AT this-line func Some() let f = x " this-line diff --git a/runtime/indent/testdir/vim.ok b/runtime/indent/testdir/vim.ok index 61369d4b93..3f53c5286c 100644 --- a/runtime/indent/testdir/vim.ok +++ b/runtime/indent/testdir/vim.ok @@ -21,8 +21,27 @@ let cmd = " END_INDENT +" START_INDENT +" INDENT_EXE let g:vim_indent_cont = 5 + +let list = [ + \ 'one', + \ 'two'] + +" END_INDENT + " START_INDENT " INDENT_EXE unlet g:vim_indent_cont + +let list = [ + 'one', + 'two', + ] +echo + +" END_INDENT + +" START_INDENT " INDENT_AT this-line func Some() let f = x " this-line diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim index 4b0867d88a..3e502c9adb 100644 --- a/runtime/indent/vim.vim +++ b/runtime/indent/vim.vim @@ -1,7 +1,7 @@ " Vim indent file " Language: Vim script " Maintainer: Bram Moolenaar -" Last Change: 2020 Sep 27 +" Last Change: 2021 Jan 06 " Only load this indent file when no other was loaded. if exists("b:did_indent") @@ -52,6 +52,7 @@ function GetVimIndentIntern() return 0 endif let prev_text = getline(lnum) + let found_cont = 0 " Add a 'shiftwidth' after :if, :while, :try, :catch, :finally, :function " and :else. Add it three times for a line that starts with '\' or '"\ ' @@ -83,6 +84,7 @@ function GetVimIndentIntern() endif if cur_text =~ s:lineContPat && v:lnum > 1 && prev_text !~ s:lineContPat + let found_cont = 1 if exists("g:vim_indent_cont") let ind = ind + g:vim_indent_cont else @@ -114,10 +116,50 @@ function GetVimIndentIntern() endif endif + " For a line starting with "}" find the matching "{". If it is at the start + " of the line align with it, probably end of a block. + " Use the mapped "%" from matchit to find the match, otherwise we may match + " a { inside a comment or string. + if cur_text =~ '^\s*}' + if maparg('%') != '' + exe v:lnum + silent! normal % + if line('.') < v:lnum && getline('.') =~ '^\s*{' + let ind = indent('.') + endif + else + " todo: use searchpair() to find a match + endif + endif + + " Below a line starting with "}" find the matching "{". If it is at the + " end of the line we must be below the end of a dictionary. + if prev_text =~ '^\s*}' + if maparg('%') != '' + exe lnum + silent! normal % + if line('.') == lnum || getline('.') !~ '^\s*{' + let ind = ind - shiftwidth() + endif + else + " todo: use searchpair() to find a match + endif + endif + + " Below a line starting with "]" we must be below the end of a list. + if prev_text =~ '^\s*]' + let ind = ind - shiftwidth() + endif + + " A line ending in "{"/"[} is most likely the start of a dict/list literal, + " indent the next line more. Not for a continuation line. + if prev_text =~ '[{[]\s*$' && !found_cont + let ind = ind + shiftwidth() + endif " Subtract a 'shiftwidth' on a :endif, :endwhile, :catch, :finally, :endtry, " :endfun, :enddef, :else and :augroup END. - if cur_text =~ '^\s*\(ene\@!\|}\|cat\|finall\|el\|aug\%[roup]\s\+[eE][nN][dD]\)' + if cur_text =~ '^\s*\(ene\@!\|cat\|finall\|el\|aug\%[roup]\s\+[eE][nN][dD]\)' let ind = ind - shiftwidth() endif diff --git a/runtime/lang/menu_tr.cp1254.vim b/runtime/lang/menu_tr.cp1254.vim index 5c2067e06b..e27de90b7a 100644 --- a/runtime/lang/menu_tr.cp1254.vim +++ b/runtime/lang/menu_tr.cp1254.vim @@ -1,3 +1,3 @@ " Menu Translations: Turkish -source :p:h/menu_tr_tr.cp1254.vim \ No newline at end of file +source :p:h/menu_tr_tr.cp1254.vim diff --git a/runtime/lang/menu_tr.iso_8859-9.vim b/runtime/lang/menu_tr.iso_8859-9.vim index 8712e6ac97..2c5158da55 100644 --- a/runtime/lang/menu_tr.iso_8859-9.vim +++ b/runtime/lang/menu_tr.iso_8859-9.vim @@ -1,3 +1,3 @@ " Menu Translations: Turkish -source :p:h/menu_tr_tr.iso_8859-9.vim \ No newline at end of file +source :p:h/menu_tr_tr.iso_8859-9.vim diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 07d5cb8d52..706a94b976 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -2,7 +2,7 @@ " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" -" Last Change: 2020 Dec 07 +" Last Change: 2021 Jan 03 " " WORK IN PROGRESS - Only the basics work " Note: On MS-Windows you need a recent version of gdb. The one included with @@ -70,9 +70,14 @@ if !exists('g:termdebugger') endif let s:pc_id = 12 -let s:break_id = 13 " breakpoint number is added to this +let s:asm_id = 13 +let s:break_id = 14 " breakpoint number is added to this let s:stopped = 1 +let s:parsing_disasm_msg = 0 +let s:asm_lines = [] +let s:asm_addr = '' + " Take a breakpoint number as used by GDB and turn it into an integer. " The breakpoint may contain a dot: 123.4 -> 123004 " The main breakpoint has a zero subid. @@ -114,6 +119,7 @@ func s:StartDebug_internal(dict) let s:ptywin = 0 let s:pid = 0 + let s:asmwin = 0 " Uncomment this line to write logging in "debuglog". " call ch_logfile('debuglog', 'w') @@ -153,6 +159,14 @@ func s:StartDebug_internal(dict) else call s:StartDebug_term(a:dict) endif + + if exists('g:termdebug_disasm_window') + if g:termdebug_disasm_window + let curwinid = win_getid(winnr()) + call s:GotoAsmwinOrCreateIt() + call win_gotoid(curwinid) + endif + endif endfunc " Use when debugger didn't start or ended. @@ -546,6 +560,14 @@ func s:GetFullname(msg) return name endfunc +" Extract the "addr" value from a gdb message with addr="0x0001234". +func s:GetAsmAddr(msg) + if a:msg !~ 'addr=' + return '' + endif + let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', '')) + return addr +endfunc func s:EndTermDebug(job, status) exe 'bwipe! ' . s:commbuf unlet s:gdbwin @@ -609,6 +631,69 @@ func s:EndPromptDebug(job, status) call ch_log("Returning from EndPromptDebug()") endfunc +" Disassembly window - added by Michael Sartain +" +" - CommOutput: disassemble $pc +" - CommOutput: &"disassemble $pc\n" +" - CommOutput: ~"Dump of assembler code for function main(int, char**):\n" +" - CommOutput: ~" 0x0000555556466f69 <+0>:\tpush rbp\n" +" ... +" - CommOutput: ~" 0x0000555556467cd0:\tpop rbp\n" +" - CommOutput: ~" 0x0000555556467cd1:\tret \n" +" - CommOutput: ~"End of assembler dump.\n" +" - CommOutput: ^done + +" - CommOutput: disassemble $pc +" - CommOutput: &"disassemble $pc\n" +" - CommOutput: &"No function contains specified address.\n" +" - CommOutput: ^error,msg="No function contains specified address." +func s:HandleDisasmMsg(msg) + if a:msg =~ '^\^done' + let curwinid = win_getid(winnr()) + if win_gotoid(s:asmwin) + silent normal! gg0"_dG + call setline(1, s:asm_lines) + set nomodified + set filetype=asm + + let lnum = search('^' . s:asm_addr) + if lnum != 0 + exe 'sign unplace ' . s:asm_id + exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' + endif + + call win_gotoid(curwinid) + endif + + let s:parsing_disasm_msg = 0 + let s:asm_lines = [] + elseif a:msg =~ '^\^error,msg=' + if s:parsing_disasm_msg == 1 + " Disassemble call ran into an error. This can happen when gdb can't + " find the function frame address, so let's try to disassemble starting + " at current PC + call s:SendCommand('disassemble $pc,+100') + endif + let s:parsing_disasm_msg = 0 + elseif a:msg =~ '\&\"disassemble \$pc' + if a:msg =~ '+100' + " This is our second disasm attempt + let s:parsing_disasm_msg = 2 + endif + else + let value = substitute(a:msg, '^\~\"[ ]*', '', '') + let value = substitute(value, '^=>[ ]*', '', '') + let value = substitute(value, '\\n\" $', '', '') + let value = substitute(value, '\\n\"$', '', '') + let value = substitute(value, ' ', '', '') + let value = substitute(value, '\\t', ' ', 'g') + + if value != '' || !empty(s:asm_lines) + call add(s:asm_lines, value) + endif + endif +endfunc + " Handle a message received from gdb on the GDB/MI interface. func s:CommOutput(chan, msg) let msgs = split(a:msg, "\r") @@ -618,7 +703,10 @@ func s:CommOutput(chan, msg) if msg[0] == "\n" let msg = msg[1:] endif - if msg != '' + + if s:parsing_disasm_msg + call s:HandleDisasmMsg(msg) + elseif msg != '' if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' call s:HandleCursor(msg) elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' @@ -631,6 +719,9 @@ func s:CommOutput(chan, msg) call s:HandleEvaluate(msg) elseif msg =~ '^\^error,msg=' call s:HandleError(msg) + elseif msg =~ '^disassemble' + let s:parsing_disasm_msg = 1 + let s:asm_lines = [] endif endif endfor @@ -671,6 +762,7 @@ func s:InstallCommands() command Gdb call win_gotoid(s:gdbwin) command Program call s:GotoProgram() command Source call s:GotoSourcewinOrCreateIt() + command Asm call s:GotoAsmwinOrCreateIt() command Winbar call s:InstallWinbar() if !exists('g:termdebug_map_K') || g:termdebug_map_K @@ -724,6 +816,7 @@ func s:DeleteCommands() delcommand Gdb delcommand Program delcommand Source + delcommand Asm delcommand Winbar if exists('s:k_map_saved') @@ -923,6 +1016,48 @@ func s:GotoSourcewinOrCreateIt() endif endfunc +func s:GotoAsmwinOrCreateIt() + if !win_gotoid(s:asmwin) + if win_gotoid(s:sourcewin) + exe 'rightbelow new' + else + exe 'new' + endif + + let s:asmwin = win_getid(winnr()) + + setlocal nowrap + setlocal number + setlocal noswapfile + setlocal buftype=nofile + + let asmbuf = bufnr('Termdebug-asm-listing') + if asmbuf > 0 + exe 'buffer' . asmbuf + else + exe 'file Termdebug-asm-listing' + endif + + if exists('g:termdebug_disasm_window') + if g:termdebug_disasm_window > 1 + exe 'resize ' . g:termdebug_disasm_window + endif + endif + endif + + if s:asm_addr != '' + let lnum = search('^' . s:asm_addr) + if lnum == 0 + if s:stopped + call s:SendCommand('disassemble $pc') + endif + else + exe 'sign unplace ' . s:asm_id + exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' + endif + endif +endfunc + " Handle stopping and running message from gdb. " Will update the sign that shows the current position. func s:HandleCursor(msg) @@ -941,6 +1076,27 @@ func s:HandleCursor(msg) else let fname = '' endif + + if a:msg =~ 'addr=' + let asm_addr = s:GetAsmAddr(a:msg) + if asm_addr != '' + let s:asm_addr = asm_addr + + let curwinid = win_getid(winnr()) + if win_gotoid(s:asmwin) + let lnum = search('^' . s:asm_addr) + if lnum == 0 + call s:SendCommand('disassemble $pc') + else + exe 'sign unplace ' . s:asm_id + exe 'sign place ' . s:asm_id . ' line=' . lnum . ' name=debugPC' + endif + + call win_gotoid(curwinid) + endif + endif + endif + if a:msg =~ '^\(\*stopped\|=thread-selected\)' && filereadable(fname) let lnum = substitute(a:msg, '.*line="\([^"]*\)".*', '\1', '') if lnum =~ '^[0-9]*$' diff --git a/runtime/syntax/abap.vim b/runtime/syntax/abap.vim index 4650109fb1..e48dfc3603 100644 --- a/runtime/syntax/abap.vim +++ b/runtime/syntax/abap.vim @@ -1,7 +1,7 @@ " Vim ABAP syntax file " Language: SAP - ABAP/R4 " Maintainer: Marius Piedallu van Wyk -" Last Change: 2018 Dec 12 +" Last Change: 2021 Jan 02 " Comment: Thanks to EPI-USE Labs for all your assistance. :) " Quit when a syntax file was already loaded @@ -193,4 +193,4 @@ hi def link abapHex Number let b:current_syntax = "abap" -" vim: ts=8 sw=2 \ No newline at end of file +" vim: ts=8 sw=2 diff --git a/runtime/syntax/prolog.vim b/runtime/syntax/prolog.vim index 58b279b029..93aba4dc19 100644 --- a/runtime/syntax/prolog.vim +++ b/runtime/syntax/prolog.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: PROLOG " Maintainer: Anton Kochkov -" Last Change: 2019 Aug 29 +" Last Change: 2021 Jan 05 " There are two sets of highlighting in here: " If the "prolog_highlighting_clean" variable exists, it is rather sparse. @@ -21,16 +21,16 @@ syn case match " Very simple highlighting for comments, clause heads and " character codes. It respects prolog strings and atoms. -syn region prologCComment start=+/\*+ end=+\*/+ -syn match prologComment +%.*+ +syn region prologCComment start=+/\*+ end=+\*/+ contains=@Spell +syn match prologComment +%.*+ contains=@Spell if !exists("prolog_highlighting_no_keyword") syn keyword prologKeyword module meta_predicate multifile dynamic endif syn match prologCharCode +0'\\\=.+ -syn region prologString start=+"+ skip=+\\\\\|\\"+ end=+"+ +syn region prologString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@Spell syn region prologAtom start=+'+ skip=+\\\\\|\\'+ end=+'+ -syn region prologClause matchgroup=prologClauseHead start=+^\s*[a-z]\w*+ matchgroup=Normal end=+\.\s\|\.$+ contains=ALLBUT,prologClause +syn region prologClause matchgroup=prologClauseHead start=+^\s*[a-z]\w*+ matchgroup=Normal end=+\.\s\|\.$+ contains=ALLBUT,prologClause contains=@NoSpell if !exists("prolog_highlighting_clean") From df4c9af7e73aa5d0fb5bf4c0e19a39b4e1d73517 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 11 Jan 2021 19:54:42 +0100 Subject: [PATCH 03/40] patch 8.2.2329: not all ways Vim can be started are tested MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Not all ways Vim can be started are tested. Solution: Add a test for different program names. (Dominique Pellé, closes #7651) --- src/testdir/test_startup.vim | 76 ++++++++++++++++++++++++++++++++++++ src/version.c | 2 + 2 files changed, 78 insertions(+) diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim index 72c43eac90..261cb5ef78 100644 --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -1002,4 +1002,80 @@ func Test_too_many_edit_args() call assert_match('^Too many edit arguments: "-"', l[1]) endfunc +" Test starting vim with various names: vim, ex, view, evim, etc. +func Test_progname() + CheckUnix + + call mkdir('Xprogname', 'p') + call writefile(['silent !date', + \ 'call writefile([mode(1), ' + \ .. '&insertmode, &diff, &readonly, &updatecount, ' + \ .. 'join(split(execute("message"), "\n")[1:])], "Xprogname_out")', + \ 'qall'], 'Xprogname_after') + + " +---------------------------------------------- progname + " | +--------------------------------- mode(1) + " | | +--------------------------- &insertmode + " | | | +---------------------- &diff + " | | | | +----------------- &readonly + " | | | | | +-------- &updatecount + " | | | | | | +--- :messages + " | | | | | | | + let expectations = { + \ 'vim': ['n', '0', '0', '0', '200', ''], + \ 'gvim': ['n', '0', '0', '0', '200', ''], + \ 'ex': ['ce', '0', '0', '0', '200', ''], + \ 'exim': ['cv', '0', '0', '0', '200', ''], + \ 'view': ['n', '0', '0', '1', '10000', ''], + \ 'gview': ['n', '0', '0', '1', '10000', ''], + \ 'evim': ['n', '1', '0', '0', '200', ''], + \ 'eview': ['n', '1', '0', '1', '10000', ''], + \ 'rvim': ['n', '0', '0', '0', '200', 'line 1: E145: Shell commands and some functionality not allowed in rvim'], + \ 'rgvim': ['n', '0', '0', '0', '200', 'line 1: E145: Shell commands and some functionality not allowed in rvim'], + \ 'rview': ['n', '0', '0', '1', '10000', 'line 1: E145: Shell commands and some functionality not allowed in rvim'], + \ 'rgview': ['n', '0', '0', '1', '10000', 'line 1: E145: Shell commands and some functionality not allowed in rvim'], + \ 'vimdiff': ['n', '0', '1', '0', '200', ''], + \ 'gvimdiff': ['n', '0', '1', '0', '200', '']} + + let prognames = ['vim', 'gvim', 'ex', 'exim', 'view', 'gview', + \ 'evim', 'eview', 'rvim', 'rgvim', 'rview', 'rgview', + \ '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 + endif + + exe 'silent !ln -s -f ' ..exepath(GetVimProg()) .. ' Xprogname/' .. progname + + let stdout_stderr = '' + if progname =~# 'g' + let stdout_stderr = system('Xprogname/'..progname..' -f --clean --not-a-term -S Xprogname_after') + else + exe 'sil !Xprogname/'..progname..' -f --clean --not-a-term -S Xprogname_after' + endif + + 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) + call assert_equal(expectations[progname], readfile('Xprogname_out'), progname) + endif + + call delete('Xprogname/' .. progname) + call delete('Xprogname_out') + endfor + + call delete('Xprogname_after') + call delete('Xprogname', 'd') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index f56da436b4..2a6e1daa01 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2329, /**/ 2328, /**/ From 9e1d9e3473f852735ffd605a0fa4d224b81a4f0c Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 11 Jan 2021 20:17:34 +0100 Subject: [PATCH 04/40] patch 8.2.2330: Vim9: crash when using :trow in a not executed block Problem: Vim9: crash when using :trow in a not executed block. Solution: Don't generate the instruction when skipping. (closes #7659) --- src/testdir/test_vim9_script.vim | 8 +++++++- src/version.c | 2 ++ src/vim9compile.c | 6 +++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 1066a1d43e..d567de75f8 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -324,7 +324,7 @@ func g:NoSuchFunc() echo 'none' endfunc -def Test_try_catch() +def Test_try_catch_throw() var l = [] try # comment add(l, '1') @@ -558,6 +558,12 @@ def Test_try_catch() assert_equal(411, n) enddef +def Test_throw_skipped() + if 0 + throw dontgethere + endif +enddef + def DeletedFunc(): list return ['delete me'] enddef diff --git a/src/version.c b/src/version.c index 2a6e1daa01..0a3cd54a91 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2330, /**/ 2329, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index bcbc57dd12..0064bd2055 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -474,8 +474,10 @@ may_generate_2STRING(int offset, cctx_T *cctx) isn_T *isn; isntype_T isntype = ISN_2STRING; garray_T *stack = &cctx->ctx_type_stack; - type_T **type = ((type_T **)stack->ga_data) + stack->ga_len + offset; + type_T **type; + RETURN_OK_IF_SKIP(cctx); + type = ((type_T **)stack->ga_data) + stack->ga_len + offset; switch ((*type)->tt_type) { // nothing to be done @@ -7461,6 +7463,8 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED) if (compile_expr0(&p, cctx) == FAIL) return NULL; + if (cctx->ctx_skip == SKIP_YES) + return p; if (may_generate_2STRING(-1, cctx) == FAIL) return NULL; if (generate_instr_drop(cctx, ISN_THROW, 1) == NULL) From 082517570d1dce2faf3baa9f752ce0858355d221 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 11 Jan 2021 21:20:18 +0100 Subject: [PATCH 05/40] patch 8.2.2331: Vim9: wrong error when modifying dict declared with :final Problem: Vim9: wrong error when modifying dict declared with :final. Solution: Do not check for writable variable when an index follows. (closes #7657) --- src/evalvars.c | 4 +-- src/proto/vim9script.pro | 2 +- src/structs.h | 2 +- src/testdir/test_vim9_assign.vim | 16 ++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 45 +++++++++++++++++++++++++------- src/vim9script.c | 15 ++++++++--- 7 files changed, 68 insertions(+), 18 deletions(-) diff --git a/src/evalvars.c b/src/evalvars.c index 742c5a34f8..644190fbe1 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -3153,7 +3153,7 @@ set_var_const( // A Vim9 script-local variable is also present in sn_all_vars and // sn_var_vals. It may set "type" from "tv". if (is_script_local && vim9script) - update_vim9_script_var(FALSE, di, tv, &type); + update_vim9_script_var(FALSE, di, flags, tv, &type); } // existing variable, need to clear the value @@ -3243,7 +3243,7 @@ set_var_const( // A Vim9 script-local variable is also added to sn_all_vars and // sn_var_vals. It may set "type" from "tv". if (is_script_local && vim9script) - update_vim9_script_var(TRUE, di, tv, &type); + update_vim9_script_var(TRUE, di, flags, tv, &type); } if (copy || tv->v_type == VAR_NUMBER || tv->v_type == VAR_FLOAT) diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro index 833d44b233..2a63b8b274 100644 --- a/src/proto/vim9script.pro +++ b/src/proto/vim9script.pro @@ -10,7 +10,7 @@ void ex_import(exarg_T *eap); int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx); 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, typval_T *tv, type_T **type); +void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T *tv, type_T **type); void hide_script_var(scriptitem_T *si, int idx, int func_defined); void free_all_script_vars(scriptitem_T *si); svar_T *find_typval_in_script(typval_T *dest); diff --git a/src/structs.h b/src/structs.h index 712382a6f6..c47e291833 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1777,7 +1777,7 @@ struct svar_S { char_u *sv_name; // points into "sn_all_vars" di_key typval_T *sv_tv; // points into "sn_vars" or "sn_all_vars" di_tv type_T *sv_type; - int sv_const; + int sv_const; // 0, ASSIGN_CONST or ASSIGN_FINAL int sv_export; // "export let var = val" }; diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 21480633c7..8dadb2c07c 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -1225,6 +1225,12 @@ def Test_var_declaration() g:dict_val = s:dict[key] enddef GetDictVal('a') + + final adict: dict = {} + def ChangeAdict() + adict.foo = 'foo' + enddef + ChangeAdict() END CheckScriptSuccess(lines) assert_equal('', g:var_uninit) @@ -1260,6 +1266,16 @@ def Test_var_declaration_fails() CheckScriptFailure(lines, 'E741:') unlet g:constvar + lines =<< trim END + vim9script + const cdict: dict = {} + def Change() + cdict.foo = 'foo' + enddef + defcompile + END + CheckScriptFailure(lines, 'E46:') + lines =<< trim END vim9script final w:finalvar = [9] diff --git a/src/version.c b/src/version.c index 0a3cd54a91..f063d7bf3f 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2331, /**/ 2330, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 0064bd2055..4b2ee07798 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2126,11 +2126,30 @@ free_locals(cctx_T *cctx) ga_clear(&cctx->ctx_locals); } +/* + * If "check_writable" is ASSIGN_CONST give an error if the variable was + * defined with :final or :const, if "check_writable" is ASSIGN_FINAL give an + * error if the variable was defined with :const. + */ + static int +check_item_writable(svar_T *sv, int check_writable, char_u *name) +{ + if ((check_writable == ASSIGN_CONST && sv->sv_const != 0) + || (check_writable == ASSIGN_FINAL + && sv->sv_const == ASSIGN_CONST)) + { + semsg(_(e_readonlyvar), name); + return FAIL; + } + return OK; +} + /* * Find "name" in script-local items of script "sid". + * Pass "check_writable" to check_item_writable(). * Returns the index in "sn_var_vals" if found. * If found but not in "sn_var_vals" returns -1. - * If not found returns -2. + * If not found or the variable is not writable returns -2. */ int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx) @@ -2151,8 +2170,8 @@ get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx) return -2; idx = sav->sav_var_vals_idx; sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; - if (check_writable && sv->sv_const) - semsg(_(e_readonlyvar), name); + if (check_item_writable(sv, check_writable, name) == FAIL) + return -2; return idx; } @@ -2168,8 +2187,8 @@ get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx) sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; if (sv->sv_tv == &di->di_tv) { - if (check_writable && sv->sv_const) - semsg(_(e_readonlyvar), name); + if (check_item_writable(sv, check_writable, name) == FAIL) + return -2; return idx; } } @@ -2466,7 +2485,7 @@ compile_load_scriptvar( if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) return FAIL; si = SCRIPT_ITEM(current_sctx.sc_sid); - idx = get_script_item_idx(current_sctx.sc_sid, name, FALSE, cctx); + idx = get_script_item_idx(current_sctx.sc_sid, name, 0, cctx); if (idx == -1 || si->sn_version != SCRIPT_VERSION_VIM9) { // variable is not in sn_var_vals: old style script. @@ -5475,6 +5494,11 @@ compile_lhs( lhs->lhs_name = vim_strnsave(var_start, lhs->lhs_varlen); if (lhs->lhs_name == NULL) return FAIL; + + if (lhs->lhs_dest_end > var_start + lhs->lhs_varlen) + // Something follows after the variable: "var[idx]" or "var.key". + lhs->lhs_has_index = TRUE; + if (heredoc) lhs->lhs_type = &t_list_string; else @@ -5576,9 +5600,11 @@ compile_lhs( lhs->lhs_scriptvar_sid = import->imp_sid; if (SCRIPT_ID_VALID(lhs->lhs_scriptvar_sid)) { + // Check writable only when no index follows. lhs->lhs_scriptvar_idx = get_script_item_idx( - lhs->lhs_scriptvar_sid, - rawname, TRUE, cctx); + lhs->lhs_scriptvar_sid, rawname, + lhs->lhs_has_index ? ASSIGN_FINAL : ASSIGN_CONST, + cctx); if (lhs->lhs_scriptvar_idx >= 0) { scriptitem_T *si = SCRIPT_ITEM( @@ -5665,7 +5691,7 @@ compile_lhs( } lhs->lhs_member_type = lhs->lhs_type; - if (lhs->lhs_dest_end > var_start + lhs->lhs_varlen) + if (lhs->lhs_has_index) { // Something follows after the variable: "var[idx]" or "var.key". // TODO: should we also handle "->func()" here? @@ -5700,7 +5726,6 @@ compile_lhs( lhs->lhs_type = &t_any; } - lhs->lhs_has_index = TRUE; if (lhs->lhs_type->tt_member == NULL) lhs->lhs_member_type = &t_any; else diff --git a/src/vim9script.c b/src/vim9script.c index b7c0f67d19..010b4bfdb3 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -257,7 +257,7 @@ find_exported( // find name in "script" // TODO: also find script-local user function - idx = get_script_item_idx(sid, name, FALSE, cctx); + idx = get_script_item_idx(sid, name, 0, cctx); if (idx >= 0) { sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; @@ -661,10 +661,16 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg) * with a hashtable) and sn_var_vals (lookup by index). * When "create" is TRUE this is a new variable, otherwise find and update an * existing variable. + * "flags" can have ASSIGN_FINAL or ASSIGN_CONST. * When "*type" is NULL use "tv" for the type and update "*type". */ void -update_vim9_script_var(int create, dictitem_T *di, typval_T *tv, type_T **type) +update_vim9_script_var( + int create, + dictitem_T *di, + int flags, + typval_T *tv, + type_T **type) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); hashitem_T *hi; @@ -686,7 +692,8 @@ update_vim9_script_var(int create, dictitem_T *di, typval_T *tv, type_T **type) return; sv->sv_tv = &di->di_tv; - sv->sv_const = (di->di_flags & DI_FLAGS_LOCK) ? ASSIGN_CONST : 0; + sv->sv_const = (flags & ASSIGN_FINAL) ? ASSIGN_FINAL + : (flags & ASSIGN_CONST) ? ASSIGN_CONST : 0; sv->sv_export = is_export; newsav->sav_var_vals_idx = si->sn_var_vals.ga_len; ++si->sn_var_vals.ga_len; @@ -864,7 +871,7 @@ check_script_var_type(typval_T *dest, typval_T *value, char_u *name) if (sv != NULL) { - if (sv->sv_const) + if (sv->sv_const != 0) { semsg(_(e_readonlyvar), name); return FAIL; From 9567efa1b4a41baca9b2266f5903d5dda7ad1e88 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Mon, 11 Jan 2021 22:16:30 +0100 Subject: [PATCH 06/40] patch 8.2.2332: Vim9: missing :endif not reported when using :windo Problem: Vim9: missing :endif not reported when using :windo. Solution: Pass a getline function to do_cmdline(). (closes #7650) --- src/scriptfile.c | 56 ++++++++++------------------------- src/structs.h | 26 ++++++++++++++++ src/testdir/test_vim9_cmd.vim | 7 +++++ src/version.c | 2 ++ src/vim9execute.c | 13 ++++++-- 5 files changed, 62 insertions(+), 42 deletions(-) diff --git a/src/scriptfile.c b/src/scriptfile.c index adc5caffc9..c8a23d55a3 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -1019,30 +1019,6 @@ ex_options( /* * ":source" and associated commands. */ -/* - * Structure used to store info for each sourced file. - * It is shared between do_source() and getsourceline(). - * This is required, because it needs to be handed to do_cmdline() and - * sourcing can be done recursively. - */ -struct source_cookie -{ - FILE *fp; // opened file for sourcing - char_u *nextline; // if not NULL: line that was read ahead - linenr_T sourcing_lnum; // line number of the source file - int finished; // ":finish" used -#ifdef USE_CRNL - int fileformat; // EOL_UNKNOWN, EOL_UNIX or EOL_DOS - int error; // TRUE if LF found after CR-LF -#endif -#ifdef FEAT_EVAL - linenr_T breakpoint; // next line with breakpoint or zero - char_u *fname; // name of sourced file - int dbg_tick; // debug_tick when breakpoint was set - int level; // top nesting level of sourced file -#endif - vimconv_T conv; // type of conversion -}; #ifdef FEAT_EVAL /* @@ -1051,7 +1027,7 @@ struct source_cookie linenr_T * source_breakpoint(void *cookie) { - return &((struct source_cookie *)cookie)->breakpoint; + return &((source_cookie_T *)cookie)->breakpoint; } /* @@ -1060,7 +1036,7 @@ source_breakpoint(void *cookie) int * source_dbg_tick(void *cookie) { - return &((struct source_cookie *)cookie)->dbg_tick; + return &((source_cookie_T *)cookie)->dbg_tick; } /* @@ -1069,7 +1045,7 @@ source_dbg_tick(void *cookie) int source_level(void *cookie) { - return ((struct source_cookie *)cookie)->level; + return ((source_cookie_T *)cookie)->level; } /* @@ -1079,7 +1055,7 @@ source_level(void *cookie) char_u * source_nextline(void *cookie) { - return ((struct source_cookie *)cookie)->nextline; + return ((source_cookie_T *)cookie)->nextline; } #endif @@ -1130,7 +1106,7 @@ do_source( int is_vimrc, // DOSO_ value int *ret_sid UNUSED) { - struct source_cookie cookie; + source_cookie_T cookie; char_u *p; char_u *fname_exp; char_u *firstline = NULL; @@ -1613,12 +1589,12 @@ get_sourced_lnum( void *cookie) { return fgetline == getsourceline - ? ((struct source_cookie *)cookie)->sourcing_lnum + ? ((source_cookie_T *)cookie)->sourcing_lnum : SOURCING_LNUM; } static char_u * -get_one_sourceline(struct source_cookie *sp) +get_one_sourceline(source_cookie_T *sp) { garray_T ga; int len; @@ -1736,7 +1712,7 @@ getsourceline( int indent UNUSED, getline_opt_T options) { - struct source_cookie *sp = (struct source_cookie *)cookie; + source_cookie_T *sp = (source_cookie_T *)cookie; char_u *line; char_u *p; int do_vim9_all = in_vim9script() @@ -1761,8 +1737,8 @@ getsourceline( SOURCING_LNUM = sp->sourcing_lnum + 1; // Get current line. If there is a read-ahead line, use it, otherwise get - // one now. - if (sp->finished) + // one now. "fp" is NULL if actually using a string. + if (sp->finished || sp->fp == NULL) line = NULL; else if (sp->nextline == NULL) line = get_one_sourceline(sp); @@ -1880,8 +1856,8 @@ getsourceline( void ex_scriptencoding(exarg_T *eap) { - struct source_cookie *sp; - char_u *name; + source_cookie_T *sp; + char_u *name; if (!getline_equal(eap->getline, eap->cookie, getsourceline)) { @@ -1899,7 +1875,7 @@ ex_scriptencoding(exarg_T *eap) name = eap->arg; // Setup for conversion from the specified encoding to 'encoding'. - sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie); + sp = (source_cookie_T *)getline_cookie(eap->getline, eap->cookie); convert_setup(&sp->conv, name, p_enc); if (name != eap->arg) @@ -1963,7 +1939,7 @@ do_finish(exarg_T *eap, int reanimate) int idx; if (reanimate) - ((struct source_cookie *)getline_cookie(eap->getline, + ((source_cookie_T *)getline_cookie(eap->getline, eap->cookie))->finished = FALSE; // Cleanup (and inactivate) conditionals, but stop when a try conditional @@ -1977,7 +1953,7 @@ do_finish(exarg_T *eap, int reanimate) report_make_pending(CSTP_FINISH, NULL); } else - ((struct source_cookie *)getline_cookie(eap->getline, + ((source_cookie_T *)getline_cookie(eap->getline, eap->cookie))->finished = TRUE; } @@ -1993,7 +1969,7 @@ source_finished( void *cookie) { return (getline_equal(fgetline, cookie, getsourceline) - && ((struct source_cookie *)getline_cookie( + && ((source_cookie_T *)getline_cookie( fgetline, cookie))->finished); } diff --git a/src/structs.h b/src/structs.h index c47e291833..d6bf6672aa 100644 --- a/src/structs.h +++ b/src/structs.h @@ -4300,6 +4300,32 @@ typedef struct int sa_wrapped; // search wrapped around } searchit_arg_T; +/* + * Cookie used by getsourceline(). + */ +/* + * Cookie used to store info for each sourced file. + * It is shared between do_source() and getsourceline(). + * This is passed to do_cmdline(). + */ +typedef struct { + FILE *fp; // opened file for sourcing + char_u *nextline; // if not NULL: line that was read ahead + linenr_T sourcing_lnum; // line number of the source file + int finished; // ":finish" used +#ifdef USE_CRNL + int fileformat; // EOL_UNKNOWN, EOL_UNIX or EOL_DOS + int error; // TRUE if LF found after CR-LF +#endif +#ifdef FEAT_EVAL + linenr_T breakpoint; // next line with breakpoint or zero + char_u *fname; // name of sourced file + int dbg_tick; // debug_tick when breakpoint was set + int level; // top nesting level of sourced file +#endif + vimconv_T conv; // type of conversion +} source_cookie_T; + #define WRITEBUFSIZE 8192 // size of normal write buffer diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index f5cb1c8739..5d66966801 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -921,4 +921,11 @@ def Test_wincmd() close enddef +def Test_windo_missing_endif() + var lines =<< trim END + windo if 1 + END + CheckDefExecFailure(lines, 'E171:', 1) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index f063d7bf3f..464154f33f 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2332, /**/ 2331, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 378b10443a..be06d89ac5 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1382,9 +1382,18 @@ call_def_function( // execute Ex command line case ISN_EXEC: { + source_cookie_T cookie; + SOURCING_LNUM = iptr->isn_lnum; - do_cmdline_cmd(iptr->isn_arg.string); - if (did_emsg) + // Pass getsourceline to get an error for a missing ":end" + // command. + CLEAR_FIELD(cookie); + cookie.sourcing_lnum = iptr->isn_lnum - 1; + if (do_cmdline(iptr->isn_arg.string, + getsourceline, &cookie, + DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED) + == FAIL + || did_emsg) goto on_error; } break; From cb6cbf29e97b7abdeb1e6cbdc5e735f5b55e97a1 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 12 Jan 2021 17:17:01 +0100 Subject: [PATCH 07/40] patch 8.2.2333: Vim9: warning for uninitialized variable Problem: Vim9: warning for uninitialized variable. (Tony Mechelynck) Solution: Initialize "res". --- src/version.c | 2 ++ src/vim9execute.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/version.c b/src/version.c index 464154f33f..390c1f92f0 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2333, /**/ 2332, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index be06d89ac5..7c4ef2a92c 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -754,7 +754,7 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx) int argcount = argcount_arg; char_u *name = NULL; int called_emsg_before = called_emsg; - int res; + int res = FAIL; if (tv->v_type == VAR_PARTIAL) { @@ -800,7 +800,7 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx) vim_free(tofree); } - if (name == NULL || res == FAIL) + if (res == FAIL) { if (called_emsg == called_emsg_before) semsg(_(e_unknownfunc), From a0122dcd1cc9e9bb62c071a9b91426a8bce4f8d9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 12 Jan 2021 17:42:24 +0100 Subject: [PATCH 08/40] patch 8.2.2334: Pascal-like filetypes not always detected Problem: Pascal-like filetypes not always detected. Solution: Improved Puppet, InstantFPC and Pascal detection. (Doug Kearns, closes #7662) --- runtime/autoload/dist/ft.vim | 23 +++++++++++++++++++++-- runtime/filetype.vim | 4 +++- runtime/scripts.vim | 4 ++++ src/testdir/test_filetype.vim | 32 +++++++++++++++++++++++++++++++- src/version.c | 2 ++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 2c3179e314..1ac74b5785 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -362,6 +362,10 @@ func dist#ft#FTinc() setf aspvbs elseif lines =~ "' + func dist#ft#FTprogress_pascal() if exists("g:filetype_p") exe "setf " . g:filetype_p @@ -419,8 +426,7 @@ func dist#ft#FTprogress_pascal() let lnum = 1 while lnum <= 10 && lnum < line('$') let line = getline(lnum) - if line =~ '^\s*\(program\|unit\|procedure\|function\|const\|type\|var\)\>' - \ || line =~ '^\s*{' || line =~ '^\s*(\*' + if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords setf pascal return elseif line !~ '^\s*$' || line =~ '^/\*' @@ -433,6 +439,19 @@ func dist#ft#FTprogress_pascal() setf progress endfunc +func dist#ft#FTpp() + if exists("g:filetype_pp") + exe "setf " . g:filetype_pp + else + let line = getline(nextnonblank(1)) + if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords + setf pascal + else + setf puppet + endif + endif +endfunc + func dist#ft#FTr() let max = line("$") > 50 ? 50 : line("$") diff --git a/runtime/filetype.vim b/runtime/filetype.vim index dddb6d11cc..7a33f8fa2b 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1174,7 +1174,9 @@ au BufNewFile,BufRead *.papp,*.pxml,*.pxsl setf papp au BufNewFile,BufRead */etc/passwd,*/etc/passwd-,*/etc/passwd.edit,*/etc/shadow,*/etc/shadow-,*/etc/shadow.edit,*/var/backups/passwd.bak,*/var/backups/shadow.bak setf passwd " Pascal (also *.p) -au BufNewFile,BufRead *.pas,*.pp setf pascal +au BufNewFile,BufRead *.pas setf pascal + +au BufNewFile,BufRead *.pp call dist#ft#FTpp() " Delphi or Lazarus program file au BufNewFile,BufRead *.dpr,*.lpr setf pascal diff --git a/runtime/scripts.vim b/runtime/scripts.vim index 9c2787ace6..9217b4416a 100644 --- a/runtime/scripts.vim +++ b/runtime/scripts.vim @@ -182,6 +182,10 @@ if s:line1 =~# "^#!" elseif s:name =~# 'clojure' set ft=clojure + " Free Pascal + elseif s:name =~# 'instantfpc\>' + set ft=pascal + endif unlet s:name diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index ea2237a76e..2c649285db 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -348,7 +348,7 @@ let s:filename_checks = { \ 'pamconf': ['/etc/pam.conf', '/etc/pam.d/file', 'any/etc/pam.conf', 'any/etc/pam.d/file'], \ 'pamenv': ['/etc/security/pam_env.conf', '/home/user/.pam_environment', '.pam_environment', 'pam_env.conf'], \ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'], - \ 'pascal': ['file.pas', 'file.pp', 'file.dpr', 'file.lpr'], + \ 'pascal': ['file.pas', 'file.dpr', 'file.lpr'], \ 'passwd': ['any/etc/passwd', 'any/etc/passwd-', 'any/etc/passwd.edit', 'any/etc/shadow', 'any/etc/shadow-', 'any/etc/shadow.edit', 'any/var/backups/passwd.bak', 'any/var/backups/shadow.bak', '/etc/passwd', '/etc/passwd-', '/etc/passwd.edit', '/etc/shadow', '/etc/shadow-', '/etc/shadow.edit', '/var/backups/passwd.bak', '/var/backups/shadow.bak'], \ 'pbtxt': ['file.pbtxt'], \ 'pccts': ['file.g'], @@ -384,6 +384,7 @@ let s:filename_checks = { \ 'proto': ['file.proto'], \ 'protocols': ['/etc/protocols', 'any/etc/protocols'], \ 'psf': ['file.psf'], + \ 'puppet': ['file.pp'], \ 'pyrex': ['file.pyx', 'file.pxd'], \ 'python': ['file.py', 'file.pyw', '.pythonstartup', '.pythonrc', 'file.ptl', 'file.pyi', 'SConstruct'], \ 'quake': ['anybaseq2/file.cfg', 'anyid1/file.cfg', 'quake3/file.cfg', 'baseq2/file.cfg', 'id1/file.cfg', 'quake1/file.cfg', 'some-baseq2/file.cfg', 'some-id1/file.cfg', 'some-quake1/file.cfg'], @@ -636,6 +637,7 @@ let s:script_checks = { \ 'cpp': [['// Standard iostream objects -*- C++ -*-'], \ ['// -*- C++ -*-']], \ 'yaml': [['%YAML 1.2']], + \ 'pascal': [['#!/path/instantfpc']], \ } " Various forms of "env" optional arguments. @@ -730,5 +732,33 @@ func Test_ts_file() filetype off endfunc +func Test_pp_file() + filetype on + + call writefile(['looks like puppet'], 'Xfile.pp') + split Xfile.pp + call assert_equal('puppet', &filetype) + bwipe! + + let g:filetype_pp = 'pascal' + split Xfile.pp + call assert_equal('pascal', &filetype) + bwipe! + + " Test dist#ft#FTpp() + call writefile(['{ pascal comment'], 'Xfile.pp') + split Xfile.pp + call assert_equal('pascal', &filetype) + bwipe! + + call writefile(['procedure pascal'], 'Xfile.pp') + split Xfile.pp + call assert_equal('pascal', &filetype) + bwipe! + + call delete('Xfile.ts') + filetype off +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 390c1f92f0..7566a3989d 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2334, /**/ 2333, /**/ From 7cd24227c02afdb4249db406e2174eda1e6b36b4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 12 Jan 2021 18:58:39 +0100 Subject: [PATCH 09/40] patch 8.2.2335: Vim9: "silent return" does not restore command modifiers Problem: Vim9: "silent return" does not restore command modifiers. Solution: Resture command modifiers before returning. (closes #7649) --- src/testdir/test_vim9_disassemble.vim | 15 +++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 5 +++++ 3 files changed, 22 insertions(+) diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim index ca9b90e886..83dcda0854 100644 --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -1842,4 +1842,19 @@ def Test_silent() res) enddef +def s:SilentReturn(): string + silent return "done" +enddef + +def Test_silent_return() + var res = execute('disass s:SilentReturn') + assert_match('\d*_SilentReturn\_s*' .. + 'silent return "done"\_s*' .. + '\d CMDMOD silent\_s*' .. + '\d PUSHS "done"\_s*' .. + '\d CMDMOD_REV\_s*' .. + '\d RETURN', + res) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 7566a3989d..1832108d39 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2335, /**/ 2334, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index 4b2ee07798..e25a36b753 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2055,6 +2055,7 @@ generate_undo_cmdmods(cctx_T *cctx) { if (cctx->ctx_has_cmdmod && generate_instr(cctx, ISN_CMDMOD_REV) == NULL) return FAIL; + cctx->ctx_has_cmdmod = FALSE; return OK; } @@ -4933,6 +4934,10 @@ compile_return(char_u *arg, int check_return_type, cctx_T *cctx) // No argument, return zero. generate_PUSHNR(cctx, 0); } + + // Undo any command modifiers. + generate_undo_cmdmods(cctx); + if (cctx->ctx_skip != SKIP_YES && generate_instr(cctx, ISN_RETURN) == NULL) return NULL; From b0e6b513648db7035046613431a4aa9d71ef4653 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 12 Jan 2021 20:23:40 +0100 Subject: [PATCH 10/40] patch 8.2.2336: Vim9: not possible to extend dictionary with different type Problem: Vim9: it is not possible to extend a dictionary with different item types. Solution: Add extendnew(). (closes #7666) --- runtime/doc/eval.txt | 10 +++++ runtime/doc/usr_41.txt | 2 + src/evalfunc.c | 20 ++++++++- src/list.c | 73 ++++++++++++++++++++++++++----- src/proto/list.pro | 1 + src/testdir/test_listdict.vim | 12 +++++ src/testdir/test_vim9_builtin.vim | 10 +++++ src/version.c | 2 + 8 files changed, 118 insertions(+), 12 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 7848534b5d..d31ba2d362 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2524,6 +2524,9 @@ expand({expr} [, {nosuf} [, {list}]]) expandcmd({expr}) String expand {expr} like with `:edit` extend({expr1}, {expr2} [, {expr3}]) List/Dict insert items of {expr2} into {expr1} +extendnew({expr1}, {expr2} [, {expr3}]) + List/Dict like |extend()| but creates a new + List or Dictionary feedkeys({string} [, {mode}]) Number add key sequence to typeahead buffer filereadable({file}) Number |TRUE| if {file} is a readable file filewritable({file}) Number |TRUE| if {file} is a writable file @@ -4520,6 +4523,13 @@ extend({expr1}, {expr2} [, {expr3}]) *extend()* mylist->extend(otherlist) +extendnew({expr1}, {expr2} [, {expr3}]) *extendnew()* + Like |extend()| but instead of adding items to {expr1} a new + List or Dictionary is created and returned. {expr1} remains + unchanged. Items can still be changed by {expr2}, if you + don't want that use |deepcopy()| first. + + feedkeys({string} [, {mode}]) *feedkeys()* Characters in {string} are queued for processing as if they come from a mapping or were typed by the user. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 7d4e3a2b09..624bb934f5 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -640,6 +640,7 @@ List manipulation: *list-functions* insert() insert an item somewhere in a List add() append an item to a List extend() append a List to a List + extendnew() make a new List and append items remove() remove one or more items from a List copy() make a shallow copy of a List deepcopy() make a full copy of a List @@ -669,6 +670,7 @@ Dictionary manipulation: *dict-functions* empty() check if Dictionary is empty remove() remove an entry from a Dictionary extend() add entries from one Dictionary to another + extendnew() make a new Dictionary and append items filter() remove selected entries from a Dictionary map() change each Dictionary entry mapnew() make a new Dictionary with changed items diff --git a/src/evalfunc.c b/src/evalfunc.c index 1abcd5e11c..0d17b9ed79 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -340,7 +340,7 @@ arg_list_or_dict(type_T *type, argcontext_T *context) } /* - * Check "type" is the same type as the previous argument + * Check "type" is the same type as the previous argument. * Must not be used for the first argcheck_T entry. */ static int @@ -351,6 +351,21 @@ arg_same_as_prev(type_T *type, argcontext_T *context) return check_arg_type(prev_type, type, context->arg_idx + 1); } +/* + * Check "type" is the same basic type as the previous argument, checks list or + * dict vs other type, but not member type. + * Must not be used for the first argcheck_T entry. + */ + static int +arg_same_struct_as_prev(type_T *type, argcontext_T *context) +{ + type_T *prev_type = context->arg_types[context->arg_idx - 1]; + + if (prev_type->tt_type != context->arg_types[context->arg_idx]->tt_type) + return check_arg_type(prev_type, type, context->arg_idx + 1); + return OK; +} + /* * Check "type" is an item of the list or blob of the previous arg. * Must not be used for the first argcheck_T entry. @@ -394,6 +409,7 @@ arg_extend3(type_T *type, argcontext_T *context) argcheck_T arg1_float_or_nr[] = {arg_float_or_nr}; argcheck_T arg2_listblob_item[] = {arg_list_or_blob, arg_item_of_prev}; argcheck_T arg23_extend[] = {arg_list_or_dict, arg_same_as_prev, arg_extend3}; +argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, arg_extend3}; argcheck_T arg3_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number}; /* @@ -877,6 +893,8 @@ static funcentry_T global_functions[] = ret_string, f_expandcmd}, {"extend", 2, 3, FEARG_1, arg23_extend, ret_first_arg, f_extend}, + {"extendnew", 2, 3, FEARG_1, arg23_extendnew, + ret_first_cont, f_extendnew}, {"feedkeys", 1, 2, FEARG_1, NULL, ret_void, f_feedkeys}, {"file_readable", 1, 1, FEARG_1, NULL, // obsolete diff --git a/src/list.c b/src/list.c index 2b44ebacb8..f7842fa875 100644 --- a/src/list.c +++ b/src/list.c @@ -2454,14 +2454,11 @@ f_count(typval_T *argvars, typval_T *rettv) } /* - * "extend(list, list [, idx])" function - * "extend(dict, dict [, action])" function + * "extend()" or "extendnew()" function. "is_new" is TRUE for extendnew(). */ - void -f_extend(typval_T *argvars, typval_T *rettv) + static void +extend(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg, int is_new) { - char_u *arg_errmsg = (char_u *)N_("extend() argument"); - if (argvars[0].v_type == VAR_LIST && argvars[1].v_type == VAR_LIST) { list_T *l1, *l2; @@ -2476,8 +2473,16 @@ f_extend(typval_T *argvars, typval_T *rettv) return; } l2 = argvars[1].vval.v_list; - if (!value_check_lock(l1->lv_lock, arg_errmsg, TRUE) && l2 != NULL) + if ((is_new || !value_check_lock(l1->lv_lock, arg_errmsg, TRUE)) + && l2 != NULL) { + if (is_new) + { + l1 = list_copy(l1, FALSE, get_copyID()); + if (l1 == NULL) + return; + } + if (argvars[2].v_type != VAR_UNKNOWN) { before = (long)tv_get_number_chk(&argvars[2], &error); @@ -2500,7 +2505,14 @@ f_extend(typval_T *argvars, typval_T *rettv) item = NULL; list_extend(l1, l2, item); - copy_tv(&argvars[0], rettv); + if (is_new) + { + rettv->v_type = VAR_LIST; + rettv->vval.v_list = l1; + rettv->v_lock = FALSE; + } + else + copy_tv(&argvars[0], rettv); } } else if (argvars[0].v_type == VAR_DICT && argvars[1].v_type == VAR_DICT) @@ -2516,8 +2528,16 @@ f_extend(typval_T *argvars, typval_T *rettv) return; } d2 = argvars[1].vval.v_dict; - if (!value_check_lock(d1->dv_lock, arg_errmsg, TRUE) && d2 != NULL) + if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE)) + && d2 != NULL) { + if (is_new) + { + d1 = dict_copy(d1, FALSE, get_copyID()); + if (d1 == NULL) + return; + } + // Check the third argument. if (argvars[2].v_type != VAR_UNKNOWN) { @@ -2540,11 +2560,42 @@ f_extend(typval_T *argvars, typval_T *rettv) dict_extend(d1, d2, action); - copy_tv(&argvars[0], rettv); + if (is_new) + { + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = d1; + rettv->v_lock = FALSE; + } + else + copy_tv(&argvars[0], rettv); } } else - semsg(_(e_listdictarg), "extend()"); + semsg(_(e_listdictarg), is_new ? "extendnew()" : "extend()"); +} + +/* + * "extend(list, list [, idx])" function + * "extend(dict, dict [, action])" function + */ + void +f_extend(typval_T *argvars, typval_T *rettv) +{ + char_u *errmsg = (char_u *)N_("extend() argument"); + + extend(argvars, rettv, errmsg, FALSE); +} + +/* + * "extendnew(list, list [, idx])" function + * "extendnew(dict, dict [, action])" function + */ + void +f_extendnew(typval_T *argvars, typval_T *rettv) +{ + char_u *errmsg = (char_u *)N_("extendnew() argument"); + + extend(argvars, rettv, errmsg, TRUE); } /* diff --git a/src/proto/list.pro b/src/proto/list.pro index 26990509ff..b77add546b 100644 --- a/src/proto/list.pro +++ b/src/proto/list.pro @@ -52,6 +52,7 @@ void f_mapnew(typval_T *argvars, typval_T *rettv); void f_add(typval_T *argvars, typval_T *rettv); void f_count(typval_T *argvars, typval_T *rettv); void f_extend(typval_T *argvars, typval_T *rettv); +void f_extendnew(typval_T *argvars, typval_T *rettv); void f_insert(typval_T *argvars, typval_T *rettv); void f_remove(typval_T *argvars, typval_T *rettv); void f_reverse(typval_T *argvars, typval_T *rettv); diff --git a/src/testdir/test_listdict.vim b/src/testdir/test_listdict.vim index 762a517080..051a37c3a5 100644 --- a/src/testdir/test_listdict.vim +++ b/src/testdir/test_listdict.vim @@ -864,6 +864,18 @@ func Test_listdict_extend() call assert_fails("call extend(g:, {'-!' : 10})", 'E461:') endfunc +func Test_listdict_extendnew() + " Test extendnew() with lists + let l = [1, 2, 3] + call assert_equal([1, 2, 3, 4, 5], extendnew(l, [4, 5])) + call assert_equal([1, 2, 3], l) + + " Test extend() with dictionaries. + let d = {'a': {'b': 'B'}} + call assert_equal({'a': {'b': 'B'}, 'c': 'cc'}, extendnew(d, {'c': 'cc'})) + call assert_equal({'a': {'b': 'B'}}, d) +endfunc + func s:check_scope_dict(x, fixed) func s:gen_cmd(cmd, x) return substitute(a:cmd, '\ but got number') + CheckDefFailure(['extendnew({a: 1}, [42])'], 'E1013: Argument 2: type mismatch, expected dict but got list') + CheckDefFailure(['extendnew([1, 2], "x")'], 'E1013: Argument 2: type mismatch, expected list but got string') + CheckDefFailure(['extendnew([1, 2], {x: 1})'], 'E1013: Argument 2: type mismatch, expected list but got dict') +enddef + def Test_extend_return_type() var l = extend([1, 2], [3]) var res = 0 diff --git a/src/version.c b/src/version.c index 1832108d39..d4b5dde22c 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2336, /**/ 2335, /**/ From 67876de7bbc4254268d8180d68203b965e25ed95 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 12 Jan 2021 20:51:24 +0100 Subject: [PATCH 11/40] patch 8.2.2337: configure test for GTK only says "no" Problem: Configure test for GTK only says "no". (Harm te Hennepe) Solution: Hint that a -def package is needed. (closes #5229) --- src/auto/configure | 4 ++++ src/configure.ac | 3 +++ src/version.c | 2 ++ 3 files changed, 9 insertions(+) diff --git a/src/auto/configure b/src/auto/configure index ebeed2cf4f..bbfaafcad4 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -9561,6 +9561,8 @@ $as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; } sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` } else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK -dev package" >&5 +$as_echo_n "checking for GTK -dev package... " >&6; } no_gtk=yes fi @@ -9814,6 +9816,8 @@ $as_echo_n "checking for GTK - version >= $min_gtk_version... " >&6; } sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` } else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK -dev package" >&5 +$as_echo_n "checking for GTK -dev package... " >&6; } no_gtk=yes fi diff --git a/src/configure.ac b/src/configure.ac index d13e3783ce..fe2a401342 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -2583,6 +2583,9 @@ AC_DEFUN(AM_PATH_GTK, sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` } else + dnl Put some text before the "no" to hint at installing the gtk-dev + dnl packages. + AC_MSG_CHECKING(for GTK -dev package) no_gtk=yes fi diff --git a/src/version.c b/src/version.c index d4b5dde22c..47a5bccfd3 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2337, /**/ 2336, /**/ From 64ed4d4398e92ac56a9bbd66d5ec992dd4c335f7 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 12 Jan 2021 21:22:31 +0100 Subject: [PATCH 12/40] patch 8.2.2338: Vim9: no error if using job_info() result wrongly Problem: Vim9: no error if using job_info() result wrongly. Solution: Adjust return type on number of arguments. (closes #7667) --- src/evalfunc.c | 9 ++++++++- src/globals.h | 1 + src/testdir/test_vim9_builtin.vim | 10 ++++++++++ src/version.c | 2 ++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 0d17b9ed79..65cb59f8d4 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -472,6 +472,13 @@ ret_dict_any(int argcount UNUSED, type_T **argtypes UNUSED) return &t_dict_any; } static type_T * +ret_job_info(int argcount, type_T **argtypes UNUSED) +{ + if (argcount == 0) + return &t_list_job; + return &t_dict_any; +} + static type_T * ret_dict_number(int argcount UNUSED, type_T **argtypes UNUSED) { return &t_dict_number; @@ -1100,7 +1107,7 @@ static funcentry_T global_functions[] = {"job_getchannel", 1, 1, FEARG_1, NULL, ret_channel, JOB_FUNC(f_job_getchannel)}, {"job_info", 0, 1, FEARG_1, NULL, - ret_dict_any, JOB_FUNC(f_job_info)}, + ret_job_info, JOB_FUNC(f_job_info)}, {"job_setoptions", 2, 2, FEARG_1, NULL, ret_void, JOB_FUNC(f_job_setoptions)}, {"job_start", 1, 2, FEARG_1, NULL, diff --git a/src/globals.h b/src/globals.h index e4e73f2107..e601fd6c45 100644 --- a/src/globals.h +++ b/src/globals.h @@ -433,6 +433,7 @@ EXTERN type_T t_dict_empty INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_unknown, NULL EXTERN type_T t_list_bool INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_bool, NULL); EXTERN type_T t_list_number INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_number, NULL); EXTERN type_T t_list_string INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_string, NULL); +EXTERN type_T t_list_job INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_job, NULL); EXTERN type_T t_list_dict_any INIT6(VAR_LIST, 0, 0, TTFLAG_STATIC, &t_dict_any, NULL); EXTERN type_T t_dict_bool INIT6(VAR_DICT, 0, 0, TTFLAG_STATIC, &t_bool, NULL); diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index c867266c2d..83b9931f24 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -314,6 +314,16 @@ def Test_extend_list_item_type() CheckScriptFailure(['vim9script'] + lines, 'E1012:', 1) enddef +def Test_job_info_return_type() + if has('job') + job_start(&shell) + var jobs = job_info() + assert_equal(v:t_list, type(jobs)) + assert_equal(v:t_dict, type(job_info(jobs[0]))) + job_stop(jobs[0]) + endif +enddef + def Wrong_dict_key_type(items: list): list return filter(items, (_, val) => get({[val]: 1}, 'x')) enddef diff --git a/src/version.c b/src/version.c index 47a5bccfd3..1b63a5e9c7 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2338, /**/ 2337, /**/ From a47e05f04a5a5c0369c949157c24d09cbe64ad6a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 12 Jan 2021 21:49:00 +0100 Subject: [PATCH 13/40] patch 8.2.2339: cannot get the type of a value as a string Problem: Cannot get the type of a value as a string. Solution: Add typename(). --- runtime/doc/eval.txt | 11 ++++++++++- runtime/doc/usr_41.txt | 3 ++- src/evalfunc.c | 2 ++ src/proto/vim9type.pro | 1 + src/testdir/test_vim9_builtin.vim | 4 ++-- src/testdir/test_vimscript.vim | 8 ++++++++ src/version.c | 2 ++ src/vim9type.c | 25 +++++++++++++++++++++++++ 8 files changed, 52 insertions(+), 4 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d31ba2d362..76c5548532 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -3016,7 +3016,8 @@ tr({src}, {fromstr}, {tostr}) String translate chars of {src} in {fromstr} trim({text} [, {mask} [, {dir}]]) String trim characters in {mask} from {text} trunc({expr}) Float truncate Float {expr} -type({name}) Number type of variable {name} +type({expr}) Number type of value {expr} +typename({expr}) String representation of the type of {expr} undofile({name}) String undo file name for {name} undotree() List undo file tree uniq({list} [, {func} [, {dict}]]) @@ -11129,6 +11130,14 @@ type({expr}) The result is a Number representing the type of {expr}. < Can also be used as a |method|: > mylist->type() + +typename({expr}) *typename()* + Return a string representation of the type of {expr}. + Example: > + echo typename([1, 2, 3]) + list + + undofile({name}) *undofile()* Return the name of the undo file that would be used for a file with name {name} when writing. This uses the 'undodir' diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 624bb934f5..5f9c8e1bdc 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -720,7 +720,8 @@ Other computation: *bitwise-function* srand() initialize seed used by rand() Variables: *var-functions* - type() type of a variable + type() type of a variable as a number + typename() type of a variable as text islocked() check if a variable is locked funcref() get a Funcref for a function reference function() get a Funcref for a function name diff --git a/src/evalfunc.c b/src/evalfunc.c index 65cb59f8d4..0e6d759c66 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -1742,6 +1742,8 @@ static funcentry_T global_functions[] = ret_float, FLOAT_FUNC(f_trunc)}, {"type", 1, 1, FEARG_1, NULL, ret_number, f_type}, + {"typename", 1, 1, FEARG_1, NULL, + ret_string, f_typename}, {"undofile", 1, 1, FEARG_1, NULL, ret_string, f_undofile}, {"undotree", 0, 0, 0, NULL, diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro index bc306687f8..316c650115 100644 --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -24,4 +24,5 @@ void common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap type_T *get_member_type_from_stack(type_T **stack_top, int count, int skip, garray_T *type_gap); char *vartype_name(vartype_T type); char *type_name(type_T *type, char **tofree); +void f_typename(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 83b9931f24..a84b7f6210 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -318,8 +318,8 @@ def Test_job_info_return_type() if has('job') job_start(&shell) var jobs = job_info() - assert_equal(v:t_list, type(jobs)) - assert_equal(v:t_dict, type(job_info(jobs[0]))) + assert_equal('list', typename(jobs)) + assert_equal('dict', typename(job_info(jobs[0]))) job_stop(jobs[0]) endif enddef diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim index dba8c73fc8..0ba933b7cb 100644 --- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -6600,6 +6600,14 @@ func Test_type() call ChangeYourMind() endfunc +func Test_typename() + call assert_equal('number', typename(123)) + call assert_equal('string', typename('x')) + call assert_equal('list', typename([123])) + call assert_equal('dict', typename(#{key: 123})) + call assert_equal('list>', typename([#{key: 123}])) +endfunc + "------------------------------------------------------------------------------- " Test 92: skipping code {{{1 "------------------------------------------------------------------------------- diff --git a/src/version.c b/src/version.c index 1b63a5e9c7..fe41ec27a0 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2339, /**/ 2338, /**/ diff --git a/src/vim9type.c b/src/vim9type.c index c65f747483..85f9bd8347 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -1170,4 +1170,29 @@ type_name(type_T *type, char **tofree) return name; } +/* + * "typename(expr)" function + */ + void +f_typename(typval_T *argvars, typval_T *rettv) +{ + garray_T type_list; + type_T *type; + char *tofree; + char *name; + + rettv->v_type = VAR_STRING; + ga_init2(&type_list, sizeof(type_T *), 10); + type = typval2type(argvars, &type_list); + name = type_name(type, &tofree); + if (tofree != NULL) + rettv->vval.v_string = (char_u *)tofree; + else + { + rettv->vval.v_string = vim_strsave((char_u *)name); + vim_free(tofree); + } + clear_type_list(&type_list); +} + #endif // FEAT_EVAL From 37487e16da7877129edee8d11b9b7f5c8df312c6 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 12 Jan 2021 22:08:53 +0100 Subject: [PATCH 14/40] patch 8.2.2340: win_execute() unexpectedly returns number zero when failing Problem: win_execute() unexpectedly returns number zero when failing. Solution: Return an empty string. (closes #7665) --- src/evalwindow.c | 4 ++++ src/testdir/test_execute_func.vim | 2 ++ src/testdir/test_vim9_builtin.vim | 5 +++++ src/version.c | 2 ++ 4 files changed, 13 insertions(+) diff --git a/src/evalwindow.c b/src/evalwindow.c index 492269ede4..e00747a2de 100644 --- a/src/evalwindow.c +++ b/src/evalwindow.c @@ -673,6 +673,10 @@ f_win_execute(typval_T *argvars, typval_T *rettv) win_T *save_curwin; tabpage_T *save_curtab; + // Return an empty string if something fails. + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (wp != NULL && tp != NULL) { pos_T curpos = wp->w_cursor; diff --git a/src/testdir/test_execute_func.vim b/src/testdir/test_execute_func.vim index e02b08eb4b..036371715c 100644 --- a/src/testdir/test_execute_func.vim +++ b/src/testdir/test_execute_func.vim @@ -89,6 +89,8 @@ func Test_win_execute() call win_gotoid(thiswin) let line = win_execute(otherwin, 'echo getline(1)') call assert_match('the new window', line) + let line = win_execute(134343, 'echo getline(1)') + call assert_equal('', line) if has('popupwin') let popupwin = popup_create('the popup win', {'line': 2, 'col': 3}) diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index a84b7f6210..40cfdcbb14 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -800,6 +800,11 @@ def Test_timer_paused() timer_stop(id) enddef +def Test_win_execute() + assert_equal("\n" .. winnr(), win_execute(win_getid(), 'echo winnr()')) + assert_equal('', win_execute(342343, 'echo winnr()')) +enddef + def Test_win_splitmove() split win_splitmove(1, 2, {vertical: true, rightbelow: true}) diff --git a/src/version.c b/src/version.c index fe41ec27a0..24462a0e67 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2340, /**/ 2339, /**/ From 1bb4de5302ba038b9c59e845b6d735e87d5681d0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 13 Jan 2021 19:48:46 +0100 Subject: [PATCH 15/40] patch 8.2.2341: expresison command line completion incomplete after "g:" Problem: Expresison command line completion shows variables but not functions after "g:". (Gary Johnson) Solution: Prefix "g:" when needed to a global function. --- src/evalfunc.c | 4 ++++ src/evalvars.c | 2 +- src/proto/evalvars.pro | 1 + src/testdir/test_cmdline.vim | 4 ++++ src/version.c | 2 ++ 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 0e6d759c66..d3ef15e0b9 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -1822,7 +1822,11 @@ get_function_name(expand_T *xp, int idx) { name = get_user_func_name(xp, idx); if (name != NULL) + { + if (*name != '<' && STRNCMP("g:", xp->xp_pattern, 2) == 0) + return cat_prefix_varname('g', name); return name; + } } if (++intidx < (int)(sizeof(global_functions) / sizeof(funcentry_T))) { diff --git a/src/evalvars.c b/src/evalvars.c index 644190fbe1..155f603b50 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -1952,7 +1952,7 @@ static int varnamebuflen = 0; /* * Function to concatenate a prefix and a variable name. */ - static char_u * + char_u * cat_prefix_varname(int prefix, char_u *name) { int len; diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro index c597ca3720..0861a8866a 100644 --- a/src/proto/evalvars.pro +++ b/src/proto/evalvars.pro @@ -26,6 +26,7 @@ void ex_unletlock(exarg_T *eap, char_u *argstart, int deep, int glv_flags, int ( int do_unlet(char_u *name, int forceit); void item_lock(typval_T *tv, int deep, int lock, int check_refcount); void del_menutrans_vars(void); +char_u *cat_prefix_varname(int prefix, char_u *name); char_u *get_user_var_name(expand_T *xp, int idx); char *get_var_special_name(int nr); dict_T *get_globvar_dict(void); diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 2a6db7de4f..cb16de1246 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -619,6 +619,10 @@ func Test_cmdline_complete_user_func() call assert_match('"func Test_cmdline_complete_user', @:) call feedkeys(":func s:ScriptL\\\"\", 'tx') call assert_match('"func \d\+_ScriptLocalFunction', @:) + + " g: prefix also works + call feedkeys(":echo g:Test_cmdline_complete_user_f\\\"\", 'tx') + call assert_match('"echo g:Test_cmdline_complete_user_func', @:) endfunc func Test_cmdline_complete_user_names() diff --git a/src/version.c b/src/version.c index 24462a0e67..78b805f024 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2341, /**/ 2340, /**/ From 9145846b6aa411e3ab5c0d145b37808654352877 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 13 Jan 2021 20:08:38 +0100 Subject: [PATCH 16/40] patch 8.2.2342: "char" functions may return wrong column in Insert mode Problem: "char" functions return the wront column in Insert mode when the cursor is beyond the end of the line. Solution: Compute the column correctly. (Yegappan Lakshmanan, closes #7669) --- src/eval.c | 33 +++++++++++----- src/evalfunc.c | 6 +-- src/testdir/test_cursor_func.vim | 67 ++++++++++++++++++++++++++++++-- src/version.c | 2 + 4 files changed, 91 insertions(+), 17 deletions(-) diff --git a/src/eval.c b/src/eval.c index a0877f649b..8115c7cb02 100644 --- a/src/eval.c +++ b/src/eval.c @@ -5056,12 +5056,14 @@ string2float( /* * Convert the specified byte index of line 'lnum' in buffer 'buf' to a * character index. Works only for loaded buffers. Returns -1 on failure. - * The index of the first character is one. + * The index of the first byte and the first character is zero. */ int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx) { char_u *str; + char_u *t; + int count; if (buf == NULL || buf->b_ml.ml_mfp == NULL) return -1; @@ -5074,15 +5076,26 @@ buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx) return -1; if (*str == NUL) - return 1; + return 0; - return mb_charlen_len(str, byteidx + 1); + // count the number of characters + t = str; + for (count = 0; *t != NUL && t <= str + byteidx; count++) + t += mb_ptr2len(t); + + // In insert mode, when the cursor is at the end of a non-empty line, + // byteidx points to the NUL character immediately past the end of the + // string. In this case, add one to the character count. + if (*t == NUL && byteidx != 0 && t == str + byteidx) + count++; + + return count - 1; } /* * Convert the specified character index of line 'lnum' in buffer 'buf' to a - * byte index. Works only for loaded buffers. Returns -1 on failure. The index - * of the first byte and the first character is one. + * byte index. Works only for loaded buffers. Returns -1 on failure. + * The index of the first byte and the first character is zero. */ int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx) @@ -5105,7 +5118,7 @@ buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx) while (*t != NUL && --charidx > 0) t += mb_ptr2len(t); - return t - str + 1; + return t - str; } /* @@ -5180,7 +5193,7 @@ var2fpos( { pos = curwin->w_cursor; if (charcol) - pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1; + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); return &pos; } if (name[0] == 'v' && name[1] == NUL) // Visual start @@ -5190,7 +5203,7 @@ var2fpos( else pos = curwin->w_cursor; if (charcol) - pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col) - 1; + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); return &pos; } if (name[0] == '\'') // mark @@ -5199,7 +5212,7 @@ var2fpos( if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) return NULL; if (charcol) - pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col) - 1; + pp->col = buf_byteidx_to_charidx(curbuf, pp->lnum, pp->col); return pp; } @@ -5300,7 +5313,7 @@ list2fpos( if (buf == NULL || buf->b_ml.ml_mfp == NULL) return FAIL; - n = buf_charidx_to_byteidx(buf, posp->lnum, n); + n = buf_charidx_to_byteidx(buf, posp->lnum, n) + 1; } posp->col = n; diff --git a/src/evalfunc.c b/src/evalfunc.c index d3ef15e0b9..59fa2b1cf6 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2748,7 +2748,7 @@ set_cursorpos(typval_T *argvars, typval_T *rettv, int charcol) semsg(_(e_invarg2), tv_get_string(&argvars[0])); col = (long)tv_get_number_chk(&argvars[1], NULL); if (charcol) - col = buf_charidx_to_byteidx(curbuf, line, col); + col = buf_charidx_to_byteidx(curbuf, line, col) + 1; if (argvars[2].v_type != VAR_UNKNOWN) coladd = (long)tv_get_number_chk(&argvars[2], NULL); } @@ -4011,8 +4011,8 @@ getpos_both( if (fp != NULL && charcol) { pos = *fp; - pos.col = buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, - pos.col) - 1; + pos.col = + buf_byteidx_to_charidx(wp->w_buffer, pos.lnum, pos.col); fp = &pos; } } diff --git a/src/testdir/test_cursor_func.vim b/src/testdir/test_cursor_func.vim index 19b3416b79..c09c6837e2 100644 --- a/src/testdir/test_cursor_func.vim +++ b/src/testdir/test_cursor_func.vim @@ -123,11 +123,18 @@ func Test_screenpos_number() bwipe! endfunc +" Save the visual start character position func SaveVisualStartCharPos() call add(g:VisualStartPos, getcharpos('v')) return '' endfunc +" Save the current cursor character position in insert mode +func SaveInsertCurrentCharPos() + call add(g:InsertCurrentPos, getcharpos('.')) + return '' +endfunc + " Test for the getcharpos() function func Test_getcharpos() call assert_fails('call getcharpos({})', 'E731:') @@ -156,16 +163,29 @@ func Test_getcharpos() vnoremap SaveVisualStartCharPos() let g:VisualStartPos = [] exe "normal 2G6lv$\ohh\o\" - call assert_equal([[0, 2, 7, 0], [0, 2, 9, 0], [0, 2, 5, 0]], g:VisualStartPos) + call assert_equal([[0, 2, 7, 0], [0, 2, 10, 0], [0, 2, 5, 0]], g:VisualStartPos) call assert_equal([0, 2, 9, 0], getcharpos('v')) let g:VisualStartPos = [] exe "normal 3Gv$\o\" - call assert_equal([[0, 3, 1, 0], [0, 3, 1, 0]], g:VisualStartPos) + call assert_equal([[0, 3, 1, 0], [0, 3, 2, 0]], g:VisualStartPos) let g:VisualStartPos = [] exe "normal 1Gv$\o\" call assert_equal([[0, 1, 1, 0], [0, 1, 1, 0]], g:VisualStartPos) vunmap + " Test for getting the position in insert mode with the cursor after the + " last character in a line + inoremap SaveInsertCurrentCharPos() + let g:InsertCurrentPos = [] + exe "normal 1GA\" + exe "normal 2GA\" + exe "normal 3GA\" + exe "normal 4GA\" + exe "normal 2G6li\" + call assert_equal([[0, 1, 1, 0], [0, 2, 10, 0], [0, 3, 2, 0], [0, 4, 10, 0], + \ [0, 2, 7, 0]], g:InsertCurrentPos) + iunmap + %bw! endfunc @@ -192,6 +212,10 @@ func Test_setcharpos() call setcharpos("'m", [0, 2, 9, 0]) normal `m call assert_equal([2, 11], [line('.'), col('.')]) + " unload the buffer and try to set the mark + let bnr = bufnr() + enew! + call assert_equal(-1, setcharpos("'m", [bnr, 2, 2, 0])) %bw! call assert_equal(-1, setcharpos('.', [10, 3, 1, 0])) @@ -202,6 +226,11 @@ func SaveVisualStartCharCol() return '' endfunc +func SaveInsertCurrentCharCol() + call add(g:InsertCurrentCol, charcol('.')) + return '' +endfunc + " Test for the charcol() function func Test_charcol() call assert_fails('call charcol({})', 'E731:') @@ -239,19 +268,36 @@ func Test_charcol() vnoremap SaveVisualStartCharCol() let g:VisualStartCol = [] exe "normal 2G6lv$\ohh\o\" - call assert_equal([7, 9, 5], g:VisualStartCol) + call assert_equal([7, 10, 5], g:VisualStartCol) call assert_equal(9, charcol('v')) let g:VisualStartCol = [] exe "normal 3Gv$\o\" - call assert_equal([1, 1], g:VisualStartCol) + call assert_equal([1, 2], g:VisualStartCol) let g:VisualStartCol = [] exe "normal 1Gv$\o\" call assert_equal([1, 1], g:VisualStartCol) vunmap + " Test for getting the column number in insert mode with the cursor after + " the last character in a line + inoremap SaveInsertCurrentCharCol() + let g:InsertCurrentCol = [] + exe "normal 1GA\" + exe "normal 2GA\" + exe "normal 3GA\" + exe "normal 4GA\" + exe "normal 2G6li\" + call assert_equal([1, 10, 2, 10, 7], g:InsertCurrentCol) + iunmap + %bw! endfunc +func SaveInsertCursorCharPos() + call add(g:InsertCursorPos, getcursorcharpos('.')) + return '' +endfunc + " Test for getcursorcharpos() func Test_getcursorcharpos() call assert_equal(getcursorcharpos(), getcursorcharpos(0)) @@ -269,6 +315,19 @@ func Test_getcursorcharpos() normal 4G9l call assert_equal([0, 4, 9, 0, 9], getcursorcharpos()) + " Test for getting the cursor position in insert mode with the cursor after + " the last character in a line + inoremap SaveInsertCursorCharPos() + let g:InsertCursorPos = [] + exe "normal 1GA\" + exe "normal 2GA\" + exe "normal 3GA\" + exe "normal 4GA\" + exe "normal 2G6li\" + call assert_equal([[0, 1, 1, 0, 1], [0, 2, 10, 0, 15], [0, 3, 2, 0, 2], + \ [0, 4, 10, 0, 10], [0, 2, 7, 0, 12]], g:InsertCursorPos) + iunmap + let winid = win_getid() normal 2G5l wincmd w diff --git a/src/version.c b/src/version.c index 78b805f024..aae7bf58da 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2342, /**/ 2341, /**/ From c423ad77ed763c11ba67729bbf63c1cf0915231f Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 13 Jan 2021 20:38:03 +0100 Subject: [PATCH 17/40] patch 8.2.2343: Vim9: return type of readfile() is any Problem: Vim9: return type of readfile() is any. Solution: Add readblob() so that readfile() can be expected to always return a list of strings. (closes #7671) --- runtime/doc/eval.txt | 21 +++++++++++++++++---- runtime/doc/usr_41.txt | 1 + src/evalfunc.c | 4 +++- src/filepath.c | 27 +++++++++++++++++++++++---- src/proto/filepath.pro | 3 ++- src/testdir/test_vim9_builtin.vim | 26 ++++++++++++++++++++++++++ src/version.c | 2 ++ 7 files changed, 74 insertions(+), 10 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 76c5548532..01a1235653 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 Jan 10 +*eval.txt* For Vim version 8.2. Last change: 2021 Jan 13 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2775,6 +2775,7 @@ pyxeval({expr}) any evaluate |python_x| expression rand([{expr}]) Number get pseudo-random number range({expr} [, {max} [, {stride}]]) List items from {expr} to {max} +readblob({fname}) Blob read a |Blob| from {fname} readdir({dir} [, {expr} [, {dict}]]) List file names in {dir} selected by {expr} readdirex({dir} [, {expr} [, {dict}]]) @@ -8265,6 +8266,14 @@ rand([{expr}]) *rand()* *random* :echo rand(seed) :echo rand(seed) % 16 " random number 0 - 15 < + +readblob({fname}) *readblob()* + Read file {fname} in binary mode and return a |Blob|. + When the file can't be opened an error message is given and + the result is an empty |Blob|. + Also see |readfile()| and |writefile()|. + + readdir({directory} [, {expr} [, {dict}]]) *readdir()* Return a list with file and directory names in {directory}. You can also use |glob()| if you don't need to do complicated @@ -8379,6 +8388,7 @@ readdirex({directory} [, {expr} [, {dict}]]) *readdirex()* Can also be used as a |method|: > GetDirName()->readdirex() < + *readfile()* readfile({fname} [, {type} [, {max}]]) Read file {fname} and return a |List|, each line of the file @@ -8390,8 +8400,6 @@ readfile({fname} [, {type} [, {max}]]) - When the last line ends in a NL an extra empty list item is added. - No CR characters are removed. - When {type} contains "B" a |Blob| is returned with the binary - data of the file unmodified. Otherwise: - CR characters that appear before a NL are removed. - Whether the last line ends in a NL or not does not matter. @@ -8409,6 +8417,9 @@ readfile({fname} [, {type} [, {max}]]) Note that without {max} the whole file is read into memory. Also note that there is no recognition of encoding. Read a file into a buffer if you need to. + Deprecated (use |readblob()| instead): When {type} contains + "B" a |Blob| is returned with the binary data of the file + unmodified. When the file can't be opened an error message is given and the result is an empty list. Also see |writefile()|. @@ -11295,9 +11306,11 @@ win_execute({id}, {command} [, {silent}]) *win_execute()* call win_execute(winid, 'set syntax=python') < Doing the same with `setwinvar()` would not trigger autocommands and not actually show syntax highlighting. + *E994* Not all commands are allowed in popup windows. - When window {id} does not exist then no error is given. + When window {id} does not exist then no error is given and + an empty string is returned. Can also be used as a |method|, the base is passed as the second argument: > diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 5f9c8e1bdc..bac064cf7d 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -820,6 +820,7 @@ System functions and manipulation of files: setenv() set an environment variable hostname() name of the system readfile() read a file into a List of lines + readblob() read a file into a Blob readdir() get a List of file names in a directory readdirex() get a List of file information in a directory writefile() write a List of lines or Blob into a file diff --git a/src/evalfunc.c b/src/evalfunc.c index 59fa2b1cf6..a891378ee8 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -1344,12 +1344,14 @@ static funcentry_T global_functions[] = ret_number, f_rand}, {"range", 1, 3, FEARG_1, NULL, ret_list_number, f_range}, + {"readblob", 1, 1, FEARG_1, NULL, + ret_blob, f_readblob}, {"readdir", 1, 3, FEARG_1, NULL, ret_list_string, f_readdir}, {"readdirex", 1, 3, FEARG_1, NULL, ret_list_dict_any, f_readdirex}, {"readfile", 1, 3, FEARG_1, NULL, - ret_any, f_readfile}, + ret_list_string, f_readfile}, {"reduce", 2, 3, FEARG_1, NULL, ret_any, f_reduce}, {"reg_executing", 0, 0, 0, NULL, diff --git a/src/filepath.c b/src/filepath.c index 0db0dcfb1f..81fe74924b 100644 --- a/src/filepath.c +++ b/src/filepath.c @@ -1640,11 +1640,11 @@ f_readdirex(typval_T *argvars, typval_T *rettv) /* * "readfile()" function */ - void -f_readfile(typval_T *argvars, typval_T *rettv) + static void +read_file_or_blob(typval_T *argvars, typval_T *rettv, int always_blob) { int binary = FALSE; - int blob = FALSE; + int blob = always_blob; int failed = FALSE; char_u *fname; FILE *fd; @@ -1796,7 +1796,8 @@ f_readfile(typval_T *argvars, typval_T *rettv) if (dest < buf) { - adjust_prevlen = (int)(buf - dest); // must be 1 or 2 + // must be 1 or 2 + adjust_prevlen = (int)(buf - dest); dest = buf; } if (readlen > p - buf + 1) @@ -1866,6 +1867,24 @@ f_readfile(typval_T *argvars, typval_T *rettv) fclose(fd); } +/* + * "readblob()" function + */ + void +f_readblob(typval_T *argvars, typval_T *rettv) +{ + read_file_or_blob(argvars, rettv, TRUE); +} + +/* + * "readfile()" function + */ + void +f_readfile(typval_T *argvars, typval_T *rettv) +{ + read_file_or_blob(argvars, rettv, FALSE); +} + /* * "resolve()" function */ diff --git a/src/proto/filepath.pro b/src/proto/filepath.pro index b502b8e494..62612117db 100644 --- a/src/proto/filepath.pro +++ b/src/proto/filepath.pro @@ -1,5 +1,6 @@ /* filepath.c */ int modify_fname(char_u *src, int tilde_file, int *usedlen, char_u **fnamep, char_u **bufp, int *fnamelen); +void shorten_dir(char_u *str); void f_chdir(typval_T *argvars, typval_T *rettv); void f_delete(typval_T *argvars, typval_T *rettv); void f_executable(typval_T *argvars, typval_T *rettv); @@ -21,10 +22,10 @@ void f_glob2regpat(typval_T *argvars, typval_T *rettv); void f_globpath(typval_T *argvars, typval_T *rettv); void f_isdirectory(typval_T *argvars, typval_T *rettv); void f_mkdir(typval_T *argvars, typval_T *rettv); -void shorten_dir(char_u *str); void f_pathshorten(typval_T *argvars, typval_T *rettv); void f_readdir(typval_T *argvars, typval_T *rettv); void f_readdirex(typval_T *argvars, typval_T *rettv); +void f_readblob(typval_T *argvars, typval_T *rettv); void f_readfile(typval_T *argvars, typval_T *rettv); void f_resolve(typval_T *argvars, typval_T *rettv); void f_tempname(typval_T *argvars, typval_T *rettv); diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 40cfdcbb14..3d474f3ed8 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -623,6 +623,32 @@ def Test_readdir() eval expand('sautest')->readdirex((e) => e.name[0] !=# '.') enddef +def Test_readblob() + var blob = 0z12341234 + writefile(blob, 'Xreadblob') + var read: blob = readblob('Xreadblob') + assert_equal(blob, read) + + var lines =<< trim END + var read: list = readblob('Xreadblob') + END + CheckDefAndScriptFailure(lines, 'E1012: Type mismatch; expected list but got blob', 1) + delete('Xreadblob') +enddef + +def Test_readfile() + var text = ['aaa', 'bbb', 'ccc'] + writefile(text, 'Xreadfile') + var read: list = readfile('Xreadfile') + assert_equal(text, read) + + var lines =<< trim END + var read: dict = readfile('Xreadfile') + END + CheckDefAndScriptFailure(lines, 'E1012: Type mismatch; expected dict but got list', 1) + delete('Xreadfile') +enddef + def Test_remove_return_type() var l = remove({one: [1, 2], two: [3, 4]}, 'one') var res = 0 diff --git a/src/version.c b/src/version.c index aae7bf58da..07df2f30b7 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2343, /**/ 2342, /**/ From 6601b62943a19d4f8818c3638440663d67a17b6a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Wed, 13 Jan 2021 21:47:15 +0100 Subject: [PATCH 18/40] patch 8.2.2344: using inclusive index for slice is not always desired Problem: Using inclusive index for slice is not always desired. Solution: Add the slice() method, which has an exclusive index. (closes #7408) --- runtime/doc/eval.txt | 20 ++++++++++++++ runtime/doc/usr_41.txt | 3 +++ src/eval.c | 45 +++++++++++++++++++++++-------- src/evalfunc.c | 2 ++ src/list.c | 13 +++++---- src/proto/eval.pro | 5 ++-- src/proto/list.pro | 2 +- src/proto/vim9execute.pro | 2 +- src/testdir/test_vim9_builtin.vim | 23 ++++++++++++++++ src/version.c | 2 ++ src/vim9execute.c | 17 ++++++------ 11 files changed, 106 insertions(+), 28 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 01a1235653..4bab6ef6f8 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -314,6 +314,9 @@ similar to -1. > :let shortlist = mylist[2:2] " List with one item: [3] :let otherlist = mylist[:] " make a copy of the List +Notice that the last index is inclusive. If you prefer using an exclusive +index use the |slice()| method. + If the first index is beyond the last item of the List or the second item is before the first item, the result is an empty list. There is no error message. @@ -1217,6 +1220,9 @@ a Number it is first converted to a String. In Vim9 script the indexes are character indexes. To use byte indexes use |strpart()|. +The item at index expr1b is included, it is inclusive. For an exclusive index +use the |slice()| function. + If expr1a is omitted zero is used. If expr1b is omitted the length of the string minus one is used. @@ -2884,6 +2890,8 @@ sign_unplacelist({list}) List unplace a list of signs simplify({filename}) String simplify filename as much as possible sin({expr}) Float sine of {expr} sinh({expr}) Float hyperbolic sine of {expr} +slice({expr}, {start} [, {end}]) String, List or Blob + slice of a String, List or Blob sort({list} [, {func} [, {dict}]]) List sort {list}, using {func} to compare sound_clear() none stop playing all sounds @@ -9862,6 +9870,18 @@ sinh({expr}) *sinh()* {only available when compiled with the |+float| feature} +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|. + When {end} is omitted the slice continues to the last item. + When {end} is -1 the last item is omitted. + + Can also be used as a |method|: > + GetList()->slice(offset) + + sort({list} [, {func} [, {dict}]]) *sort()* *E702* Sort the items in {list} in-place. Returns {list}. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index bac064cf7d..5d4686ae04 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -619,6 +619,8 @@ String manipulation: *string-functions* submatch() get a specific match in ":s" and substitute() strpart() get part of a string using byte index strcharpart() get part of a string using char index + slice() take a slice of a string, using char index in + Vim9 script strgetchar() get character from a string using char index expand() expand special keywords expandcmd() expand a command like done for `:edit` @@ -648,6 +650,7 @@ List manipulation: *list-functions* map() change each List item mapnew() make a new List with changed items reduce() reduce a List to a value + slice() take a slice of a List sort() sort a List reverse() reverse the order of a List uniq() remove copies of repeated adjacent items diff --git a/src/eval.c b/src/eval.c index 8115c7cb02..84f6c2bf27 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3877,8 +3877,9 @@ eval_index( if (evaluate) { int res = eval_index_inner(rettv, range, - empty1 ? NULL : &var1, empty2 ? NULL : &var2, + empty1 ? NULL : &var1, empty2 ? NULL : &var2, FALSE, key, keylen, verbose); + if (!empty1) clear_tv(&var1); if (range) @@ -3937,10 +3938,27 @@ check_can_index(typval_T *rettv, int evaluate, int verbose) return OK; } +/* + * slice() function + */ + void +f_slice(typval_T *argvars, typval_T *rettv) +{ + if (check_can_index(argvars, TRUE, FALSE) == OK) + { + copy_tv(argvars, rettv); + eval_index_inner(rettv, TRUE, argvars + 1, + argvars[2].v_type == VAR_UNKNOWN ? NULL : argvars + 2, + TRUE, NULL, 0, FALSE); + } +} + /* * Apply index or range to "rettv". * "var1" is the first index, NULL for [:expr]. * "var2" is the second index, NULL for [expr] and [expr: ] + * "exclusive" is TRUE for slice(): second index is exclusive, use character + * index for string. * Alternatively, "key" is not NULL, then key[keylen] is the dict index. */ int @@ -3949,12 +3967,13 @@ eval_index_inner( int is_range, typval_T *var1, typval_T *var2, + int exclusive, char_u *key, int keylen, int verbose) { - long n1, n2 = 0; - long len; + varnumber_T n1, n2 = 0; + long len; n1 = 0; if (var1 != NULL && rettv->v_type != VAR_DICT) @@ -3968,10 +3987,10 @@ eval_index_inner( emsg(_(e_cannot_slice_dictionary)); return FAIL; } - if (var2 == NULL) - n2 = -1; - else + if (var2 != NULL) n2 = tv_get_number(var2); + else + n2 = VARNUM_MAX; } switch (rettv->v_type) @@ -3994,10 +4013,10 @@ eval_index_inner( char_u *s = tv_get_string(rettv); len = (long)STRLEN(s); - if (in_vim9script()) + if (in_vim9script() || exclusive) { if (is_range) - s = string_slice(s, n1, n2); + s = string_slice(s, n1, n2, exclusive); else s = char_from_string(s, n1); } @@ -4015,6 +4034,8 @@ eval_index_inner( n2 = len + n2; else if (n2 >= len) n2 = len; + if (exclusive) + --n2; if (n1 >= len || n2 < 0 || n1 > n2) s = NULL; else @@ -4051,7 +4072,9 @@ eval_index_inner( if (n2 < 0) n2 = len + n2; else if (n2 >= len) - n2 = len - 1; + n2 = len - (exclusive ? 0 : 1); + if (exclusive) + --n2; if (n1 >= len || n2 < 0 || n1 > n2) { clear_tv(rettv); @@ -4103,9 +4126,9 @@ eval_index_inner( if (var1 == NULL) n1 = 0; if (var2 == NULL) - n2 = -1; + n2 = VARNUM_MAX; if (list_slice_or_index(rettv->vval.v_list, - is_range, n1, n2, rettv, verbose) == FAIL) + is_range, n1, n2, exclusive, rettv, verbose) == FAIL) return FAIL; break; diff --git a/src/evalfunc.c b/src/evalfunc.c index a891378ee8..0e800c2b6f 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -1500,6 +1500,8 @@ static funcentry_T global_functions[] = ret_float, FLOAT_FUNC(f_sin)}, {"sinh", 1, 1, FEARG_1, NULL, ret_float, FLOAT_FUNC(f_sinh)}, + {"slice", 2, 3, FEARG_1, NULL, + ret_first_arg, f_slice}, {"sort", 1, 3, FEARG_1, NULL, ret_first_arg, f_sort}, {"sound_clear", 0, 0, 0, NULL, diff --git a/src/list.c b/src/list.c index f7842fa875..0bca0b5530 100644 --- a/src/list.c +++ b/src/list.c @@ -905,14 +905,15 @@ list_slice(list_T *ol, long n1, long n2) list_slice_or_index( list_T *list, int range, - long n1_arg, - long n2_arg, + varnumber_T n1_arg, + varnumber_T n2_arg, + int exclusive, typval_T *rettv, int verbose) { long len = list_len(list); - long n1 = n1_arg; - long n2 = n2_arg; + varnumber_T n1 = n1_arg; + varnumber_T n2 = n2_arg; typval_T var1; if (n1 < 0) @@ -936,7 +937,9 @@ list_slice_or_index( if (n2 < 0) n2 = len + n2; else if (n2 >= len) - n2 = len - 1; + n2 = len - (exclusive ? 0 : 1); + if (exclusive) + --n2; if (n2 < 0 || n2 + 1 < n1) n2 = -1; l = list_slice(list, n1, n2); diff --git a/src/proto/eval.pro b/src/proto/eval.pro index 66aa430334..f611a0efa7 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -41,7 +41,8 @@ void eval_addblob(typval_T *tv1, typval_T *tv2); int eval_addlist(typval_T *tv1, typval_T *tv2); int eval_leader(char_u **arg, int vim9); int check_can_index(typval_T *rettv, int evaluate, int verbose); -int eval_index_inner(typval_T *rettv, int is_range, typval_T *var1, typval_T *var2, char_u *key, int keylen, int verbose); +void f_slice(typval_T *argvars, typval_T *rettv); +int eval_index_inner(typval_T *rettv, int is_range, typval_T *var1, typval_T *var2, int exclusive, char_u *key, int keylen, int verbose); char_u *partial_name(partial_T *pt); void partial_unref(partial_T *pt); int get_copyID(void); @@ -58,7 +59,7 @@ int string2float(char_u *text, float_T *value); int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx); int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx); pos_T *var2fpos(typval_T *varp, int dollar_lnum, int *fnum, int charcol); -int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, int char_col); +int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, int charcol); int get_env_len(char_u **arg); int get_id_len(char_u **arg); int get_name_len(char_u **arg, char_u **alias, int evaluate, int verbose); diff --git a/src/proto/list.pro b/src/proto/list.pro index b77add546b..7c9ddaef13 100644 --- a/src/proto/list.pro +++ b/src/proto/list.pro @@ -34,7 +34,7 @@ void f_flatten(typval_T *argvars, typval_T *rettv); int list_extend(list_T *l1, list_T *l2, listitem_T *bef); int list_concat(list_T *l1, list_T *l2, typval_T *tv); list_T *list_slice(list_T *ol, long n1, long n2); -int list_slice_or_index(list_T *list, int range, long n1_arg, long n2_arg, typval_T *rettv, int verbose); +int list_slice_or_index(list_T *list, int range, varnumber_T n1_arg, varnumber_T n2_arg, int exclusive, typval_T *rettv, int verbose); list_T *list_copy(list_T *orig, int deep, int copyID); void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2); char_u *list2string(typval_T *tv, int copyID, int restore_copyID); diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro index 2337a6c450..212428219d 100644 --- a/src/proto/vim9execute.pro +++ b/src/proto/vim9execute.pro @@ -2,7 +2,7 @@ void to_string_error(vartype_T vartype); void funcstack_check_refcount(funcstack_T *funcstack); char_u *char_from_string(char_u *str, varnumber_T index); -char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last); +char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive); int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx); int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv); void ex_disassemble(exarg_T *eap); diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 3d474f3ed8..f1c5de14e0 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -741,6 +741,29 @@ def Test_setreg() getreginfo('a')->assert_equal(reginfo) enddef +def Test_slice() + assert_equal('12345', slice('012345', 1)) + assert_equal('123', slice('012345', 1, 4)) + assert_equal('1234', slice('012345', 1, -1)) + assert_equal('1', slice('012345', 1, -4)) + assert_equal('', slice('012345', 1, -5)) + assert_equal('', slice('012345', 1, -6)) + + assert_equal([1, 2, 3, 4, 5], slice(range(6), 1)) + assert_equal([1, 2, 3], slice(range(6), 1, 4)) + assert_equal([1, 2, 3, 4], slice(range(6), 1, -1)) + assert_equal([1], slice(range(6), 1, -4)) + assert_equal([], slice(range(6), 1, -5)) + assert_equal([], slice(range(6), 1, -6)) + + assert_equal(0z1122334455, slice(0z001122334455, 1)) + assert_equal(0z112233, slice(0z001122334455, 1, 4)) + assert_equal(0z11223344, slice(0z001122334455, 1, -1)) + assert_equal(0z11, slice(0z001122334455, 1, -4)) + assert_equal(0z, slice(0z001122334455, 1, -5)) + assert_equal(0z, slice(0z001122334455, 1, -6)) +enddef + def Test_spellsuggest() if !has('spell') MissingFeature 'spell' diff --git a/src/version.c b/src/version.c index 07df2f30b7..dc1b7523ca 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2344, /**/ 2343, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 7c4ef2a92c..938fc2e191 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -965,10 +965,11 @@ char_idx2byte(char_u *str, size_t str_len, varnumber_T idx) /* * Return the slice "str[first:last]" using character indexes. + * "exclusive" is TRUE for slice(). * Return NULL when the result is empty. */ char_u * -string_slice(char_u *str, varnumber_T first, varnumber_T last) +string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive) { long start_byte, end_byte; size_t slen; @@ -979,12 +980,12 @@ string_slice(char_u *str, varnumber_T first, varnumber_T last) start_byte = char_idx2byte(str, slen, first); if (start_byte < 0) start_byte = 0; // first index very negative: use zero - if (last == -1) + if ((last == -1 && !exclusive) || last == VARNUM_MAX) end_byte = (long)slen; else { end_byte = char_idx2byte(str, slen, last); - if (end_byte >= 0 && end_byte < (long)slen) + if (!exclusive && end_byte >= 0 && end_byte < (long)slen) // end index is inclusive end_byte += MB_CPTR2LEN(str + end_byte); } @@ -2992,7 +2993,7 @@ call_def_function( tv = STACK_TV_BOT(-1); if (is_slice) // Slice: Select the characters from the string - res = string_slice(tv->vval.v_string, n1, n2); + 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 @@ -3030,8 +3031,8 @@ call_def_function( ectx.ec_stack.ga_len -= is_slice ? 2 : 1; tv = STACK_TV_BOT(-1); SOURCING_LNUM = iptr->isn_lnum; - if (list_slice_or_index(list, is_slice, n1, n2, tv, TRUE) - == FAIL) + if (list_slice_or_index(list, is_slice, n1, n2, FALSE, + tv, TRUE) == FAIL) goto on_error; } break; @@ -3052,8 +3053,8 @@ call_def_function( goto on_error; var1 = is_slice ? STACK_TV_BOT(-2) : STACK_TV_BOT(-1); var2 = is_slice ? STACK_TV_BOT(-1) : NULL; - res = eval_index_inner(tv, is_slice, - var1, var2, NULL, -1, TRUE); + res = eval_index_inner(tv, is_slice, var1, var2, + FALSE, NULL, -1, TRUE); clear_tv(var1); if (is_slice) clear_tv(var2); From 681fc3fa782e99fe69ed2c83c3e29109d2d61e1a Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 14 Jan 2021 17:35:21 +0100 Subject: [PATCH 19/40] patch 8.2.2345: no focus events in a terminal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: No focus events in a terminal. Solution: Add the t_fd and t_fe termcap entries and implement detecting focus events. (Hayaki Saito, Magnus Groß, closes #7673, closes #609, closes #5526) --- runtime/doc/term.txt | 14 +++++++ src/optiondefs.h | 2 + src/term.c | 88 +++++++++++++++++++++++++++++++++++++++++++- src/term.h | 8 +++- src/version.c | 2 + 5 files changed, 111 insertions(+), 3 deletions(-) diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt index 5b7850fe31..0456a8b89b 100644 --- a/runtime/doc/term.txt +++ b/runtime/doc/term.txt @@ -373,6 +373,10 @@ Added by Vim (there are no standard codes for these): t_Ri restore icon text from stack *t_Ri* *'t_Ri'* t_TE end of "raw" mode *t_TE* *'t_TE'* t_TI put terminal into "raw" mode *t_TI* *'t_TI'* + t_fd disable focus-event tracking *t_TI* *'t_TI'* + |xterm-focus-event| + t_fe enable focus-event tracking *t_TI* *'t_TI'* + |xterm-focus-event| Some codes have a start, middle and end part. The start and end are defined by the termcap option, the middle part is text. @@ -546,6 +550,16 @@ And run "xrdb -merge .Xresources" to make it effective. You can check the value with the context menu (right mouse button while CTRL key is pressed), there should be a tick at allow-window-ops. + *xterm-focus-event* +Some terminals including xterm support the focus event tracking feature. +If this feature is enabled by the 't_fe' sequence, special key sequences are +sent from the terminal to Vim every time the terminal gains or loses focus. +Vim fires focus events (|FocusGained|/|FocusLost|) by handling them accordingly. +Focus event tracking is disabled by a 't_fd' sequence when exiting "raw" mode. +If you would like to disable this feature, add the following to your .vimrc: + `set t_fd=` + `set t_fe=` + *termcap-colors* Note about colors: The 't_Co' option tells Vim the number of colors available. When it is non-zero, the 't_AB' and 't_AF' options are used to set the color. diff --git a/src/optiondefs.h b/src/optiondefs.h index 9afa84df5c..6cea0177cb 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -2957,6 +2957,8 @@ static struct vimoption options[] = p_term("t_EC", T_CEC) p_term("t_EI", T_CEI) p_term("t_fs", T_FS) + p_term("t_fd", T_FD) + p_term("t_fe", T_FE) p_term("t_GP", T_CGP) p_term("t_IE", T_CIE) p_term("t_IS", T_CIS) diff --git a/src/term.c b/src/term.c index 9e9a67b921..bf33a1c185 100644 --- a/src/term.c +++ b/src/term.c @@ -196,6 +196,11 @@ static char_u *vim_tgetstr(char *s, char_u **pp); static int detected_8bit = FALSE; // detected 8-bit terminal +#if (defined(UNIX) || defined(VMS)) +static int focus_mode = FALSE; // xterm's "focus reporting" availability +static int focus_state = FALSE; // TRUE if the terminal window gains focus +#endif + #ifdef FEAT_TERMRESPONSE // When the cursor shape was detected these values are used: // 1: block, 2: underline, 3: vertical bar @@ -908,6 +913,10 @@ static struct builtin_term builtin_termcaps[] = {(int)KS_CRT, IF_EB("\033[23;2t", ESC_STR "[23;2t")}, {(int)KS_SSI, IF_EB("\033[22;1t", ESC_STR "[22;1t")}, {(int)KS_SRI, IF_EB("\033[23;1t", ESC_STR "[23;1t")}, +# if (defined(UNIX) || defined(VMS)) + {(int)KS_FD, IF_EB("\033[?1004l", ESC_STR "[?1004l")}, + {(int)KS_FE, IF_EB("\033[?1004h", ESC_STR "[?1004h")}, +# endif {K_UP, IF_EB("\033O*A", ESC_STR "O*A")}, {K_DOWN, IF_EB("\033O*B", ESC_STR "O*B")}, @@ -2044,6 +2053,27 @@ set_termname(char_u *term) set_mouse_termcode(KS_MOUSE, (char_u *)"\233M"); #endif +#if (defined(UNIX) || defined(VMS)) + // focus reporting is supported by xterm compatible terminals and tmux. + if (use_xterm_like_mouse(term)) + { + char_u name[3]; + name[0] = (int)KS_EXTRA; + name[2] = NUL; + + // handle focus in event + name[1] = (int)KE_FOCUSGAINED; + add_termcode(name, (char_u *)"\033[I", FALSE); + + // handle focus out event + name[1] = (int)KE_FOCUSLOST; + add_termcode(name, (char_u *)"\033[O", FALSE); + + focus_mode = TRUE; + focus_state = TRUE; + } +#endif + #ifdef USE_TERM_CONSOLE // DEFAULT_TERM indicates that it is the machine console. if (STRCMP(term, DEFAULT_TERM) != 0) @@ -2519,7 +2549,10 @@ out_flush(void) if (ch_log_output) { out_buf[len] = NUL; - ch_log(NULL, "raw terminal output: \"%s\"", out_buf); + ch_log(NULL, "raw %s output: \"%s\"", + (gui.in_use && !gui.dying && !gui.starting) + ? "GUI" : "terminal", + out_buf); ch_log_output = FALSE; } #endif @@ -3582,6 +3615,13 @@ starttermcap(void) out_str(T_CTI); // start "raw" mode out_str(T_KS); // start "keypad transmit" mode out_str(T_BE); // enable bracketed paste mode + +#if (defined(UNIX) || defined(VMS)) + // enable xterm's focus reporting mode + if (focus_mode && *T_FE != NUL) + out_str(T_FE); +#endif + out_flush(); termcap_active = TRUE; screen_start(); // don't know where cursor is now @@ -3633,6 +3673,13 @@ stoptermcap(void) #ifdef FEAT_JOB_CHANNEL ch_log_output = TRUE; #endif + +#if (defined(UNIX) || defined(VMS)) + // disable xterm's focus reporting mode + if (focus_mode && *T_FD != NUL) + out_str(T_FD); +#endif + out_str(T_BD); // disable bracketed paste mode out_str(T_KE); // stop "keypad transmit" mode out_flush(); @@ -5647,6 +5694,45 @@ check_termcode( # endif // !USE_ON_FLY_SCROLL #endif // FEAT_GUI +#if (defined(UNIX) || defined(VMS)) + /* + * Handle FocusIn/FocusOut event sequences reported by XTerm. + * (CSI I/CSI O) + */ + if (focus_mode +# ifdef FEAT_GUI + && !gui.in_use +# endif + && key_name[0] == KS_EXTRA + ) + { + int did_aucmd = FALSE; + + if (key_name[1] == KE_FOCUSGAINED && !focus_state) + { + did_aucmd = apply_autocmds(EVENT_FOCUSGAINED, + NULL, NULL, FALSE, curbuf); + did_cursorhold = TRUE; + focus_state = TRUE; + key_name[1] = (int)KE_IGNORE; + } + else if (key_name[1] == KE_FOCUSLOST && focus_state) + { + did_aucmd = apply_autocmds(EVENT_FOCUSLOST, + NULL, NULL, FALSE, curbuf); + did_cursorhold = TRUE; + focus_state = FALSE; + key_name[1] = (int)KE_IGNORE; + } + if (did_aucmd && (State & (NORMAL | INSERT | TERMINAL))) + { + // in case a message was displayed: reposition the cursor + setcursor(); + out_flush(); + } + } +#endif + /* * Change to , to , etc. */ diff --git a/src/term.h b/src/term.h index bff901e3cc..a3d8299990 100644 --- a/src/term.h +++ b/src/term.h @@ -109,10 +109,12 @@ enum SpecialKey KS_CST, // save window title KS_CRT, // restore window title KS_SSI, // save icon text - KS_SRI // restore icon text + KS_SRI, // restore icon text + KS_FD, // disable focus event tracking + KS_FE // enable focus event tracking }; -#define KS_LAST KS_SRI +#define KS_LAST KS_FE /* * the terminal capabilities are stored in this array @@ -212,6 +214,8 @@ extern char_u *(term_strings[]); // current terminal strings #define T_CRT (TERM_STR(KS_CRT)) // restore window title #define T_SSI (TERM_STR(KS_SSI)) // save icon text #define T_SRI (TERM_STR(KS_SRI)) // restore icon text +#define T_FD (TERM_STR(KS_FD)) // disable focus event tracking +#define T_FE (TERM_STR(KS_FE)) // enable focus event tracking typedef enum { TMODE_COOK, // terminal mode for external cmds and Ex mode diff --git a/src/version.c b/src/version.c index dc1b7523ca..30a550fe2d 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2345, /**/ 2344, /**/ From 845b72854de90de13879598df53f1c388e52e1ba Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 14 Jan 2021 17:55:59 +0100 Subject: [PATCH 20/40] patch 8.2.2346: Codecov reports every little coverage drop Problem: Codecov reports every little coverage drop. Solution: Tolerate a 0.05% drop. Hide the appveyor config file. (Ozaki Kiichi, closes #7678) --- appveyor.yml => .appveyor.yml | 0 .codecov.yml | 6 ++++++ src/version.c | 2 ++ 3 files changed, 8 insertions(+) rename appveyor.yml => .appveyor.yml (100%) create mode 100644 .codecov.yml diff --git a/appveyor.yml b/.appveyor.yml similarity index 100% rename from appveyor.yml rename to .appveyor.yml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000000..6140fe9669 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,6 @@ +coverage: + range: "80...100" + status: + project: + default: + threshold: 0.05% diff --git a/src/version.c b/src/version.c index 30a550fe2d..56522d9343 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2346, /**/ 2345, /**/ From e1ee58ac788508585f60c91fcf6629e2251a9220 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 14 Jan 2021 19:04:44 +0100 Subject: [PATCH 21/40] patch 8.2.2347: build failure without GUI Problem: Build failure without GUI. Solution: Add #ifdef. --- src/term.c | 6 ++++-- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/term.c b/src/term.c index bf33a1c185..f4f54e2a5a 100644 --- a/src/term.c +++ b/src/term.c @@ -2550,8 +2550,10 @@ out_flush(void) { out_buf[len] = NUL; ch_log(NULL, "raw %s output: \"%s\"", - (gui.in_use && !gui.dying && !gui.starting) - ? "GUI" : "terminal", +# ifdef FEAT_GUI + (gui.in_use && !gui.dying && !gui.starting) ? "GUI" : +# endif + "terminal", out_buf); ch_log_output = FALSE; } diff --git a/src/version.c b/src/version.c index 56522d9343..319e65eb4b 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2347, /**/ 2346, /**/ From fadd55bd633b3b84d41a691e953f569bb6c21689 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 14 Jan 2021 19:19:18 +0100 Subject: [PATCH 22/40] patch 8.2.2348: no check for modified files after focus gained Problem: No check for modified files after focus gained. (Mathias Stearn) Solution: Call ui_focus_change(). --- src/term.c | 14 ++------------ src/ui.c | 2 -- src/version.c | 2 ++ 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/term.c b/src/term.c index f4f54e2a5a..d3d0581d66 100644 --- a/src/term.c +++ b/src/term.c @@ -5708,30 +5708,20 @@ check_termcode( && key_name[0] == KS_EXTRA ) { - int did_aucmd = FALSE; - if (key_name[1] == KE_FOCUSGAINED && !focus_state) { - did_aucmd = apply_autocmds(EVENT_FOCUSGAINED, - NULL, NULL, FALSE, curbuf); + ui_focus_change(TRUE); did_cursorhold = TRUE; focus_state = TRUE; key_name[1] = (int)KE_IGNORE; } else if (key_name[1] == KE_FOCUSLOST && focus_state) { - did_aucmd = apply_autocmds(EVENT_FOCUSLOST, - NULL, NULL, FALSE, curbuf); + ui_focus_change(FALSE); did_cursorhold = TRUE; focus_state = FALSE; key_name[1] = (int)KE_IGNORE; } - if (did_aucmd && (State & (NORMAL | INSERT | TERMINAL))) - { - // in case a message was displayed: reposition the cursor - setcursor(); - out_flush(); - } } #endif diff --git a/src/ui.c b/src/ui.c index ed565276c8..3f4304f692 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1101,7 +1101,6 @@ check_row(int row) return row; } -#if defined(FEAT_GUI) || defined(MSWIN) || defined(PROTO) /* * Called when focus changed. Used for the GUI or for systems where this can * be done in the console (Win32). @@ -1164,7 +1163,6 @@ ui_focus_change( maketitle(); #endif } -#endif #if defined(HAVE_INPUT_METHOD) || defined(PROTO) /* diff --git a/src/version.c b/src/version.c index 319e65eb4b..cfc929073a 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2348, /**/ 2347, /**/ From 2415669348c455df0d1b9bf55b17a06d2f990c19 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 14 Jan 2021 20:35:49 +0100 Subject: [PATCH 23/40] patch 8.2.2349: Vim9: cannot handle line break after parenthesis at line end Problem: Vim9: cannot handle line break after parenthesis at line end. Solution: Skip over line break. (closes #7677) --- src/testdir/test_vim9_expr.vim | 32 ++++++++++++++++++++------------ src/version.c | 2 ++ src/vim9compile.c | 4 +++- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 50537c858a..a45d0eae6e 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -2523,18 +2523,26 @@ enddef def Test_expr7_parens() # (expr) - assert_equal(4, (6 * 4) / 6) - assert_equal(0, 6 * ( 4 / 6 )) - - assert_equal(6, +6) - assert_equal(-6, -6) - assert_equal(false, !-3) - assert_equal(true, !+0) -enddef - -def Test_expr7_parens_vim9script() var lines =<< trim END - vim9script + assert_equal(4, (6 * 4) / 6) + assert_equal(0, 6 * ( 4 / 6 )) + + assert_equal(6, +6) + assert_equal(-6, -6) + assert_equal(false, !-3) + assert_equal(true, !+0) + + assert_equal(7, 5 + ( + 2)) + assert_equal(7, 5 + ( + 2 + )) + assert_equal(7, 5 + ( # comment + 2)) + assert_equal(7, 5 + ( # comment + # comment + 2)) + var s = ( 'one' .. @@ -2542,7 +2550,7 @@ def Test_expr7_parens_vim9script() ) assert_equal('onetwo', s) END - CheckScriptSuccess(lines) + CheckDefAndScriptSuccess(lines) enddef def Test_expr7_negate_add() diff --git a/src/version.c b/src/version.c index cfc929073a..48ba60f2f0 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2349, /**/ 2348, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index e25a36b753..b0b5973dd5 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -3554,8 +3554,10 @@ compile_leader(cctx_T *cctx, int numeric_only, char_u *start, char_u **end) compile_parenthesis(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) { int ret; + char_u *p = *arg + 1; - *arg = skipwhite(*arg + 1); + if (may_get_next_line_error(p, arg, cctx) == FAIL) + return FAIL; if (ppconst->pp_used <= PPSIZE - 10) { ret = compile_expr1(arg, cctx, ppconst); From 033135eb8eccd00c9ee72c6c0cf4b8b9f81bd269 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 14 Jan 2021 21:40:22 +0100 Subject: [PATCH 24/40] patch 8.2.2350: using "void" for no reason Problem: Using "void" for no reason. Solution: Use "char *". --- src/ex_docmd.c | 4 ++-- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index a71adb874c..7fe33d8e5f 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -1268,7 +1268,7 @@ do_cmdline( */ if (did_throw) { - void *p = NULL; + char *p = NULL; msglist_T *messages = NULL, *next; /* @@ -1283,7 +1283,7 @@ do_cmdline( vim_snprintf((char *)IObuff, IOSIZE, _("E605: Exception not caught: %s"), current_exception->value); - p = vim_strsave(IObuff); + p = (char *)vim_strsave(IObuff); break; case ET_ERROR: messages = current_exception->messages; diff --git a/src/version.c b/src/version.c index 48ba60f2f0..616f4ede85 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2350, /**/ 2349, /**/ From 8f81b22e8691f6e7c76153e945bbef15a8190cd9 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 14 Jan 2021 21:47:06 +0100 Subject: [PATCH 25/40] patch 8.2.2351: Vim9: error msg for "throw" in function called with "silent!" Problem: Vim9: error message for "throw" in function that was called with "silent!". Solution: Do not throw the exception when not caught or displayed. (closes #7672) --- src/testdir/test_vim9_script.vim | 13 +++++++++++++ src/version.c | 2 ++ src/vim9execute.c | 11 +++++++++++ 3 files changed, 26 insertions(+) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index d567de75f8..158c64c27c 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -564,6 +564,19 @@ def Test_throw_skipped() endif enddef +def Test_nocatch_throw_silenced() + var lines =<< trim END + vim9script + def Func() + throw 'error' + enddef + silent! Func() + END + writefile(lines, 'XthrowSilenced') + source XthrowSilenced + delete('XthrowSilenced') +enddef + def DeletedFunc(): list return ['delete me'] enddef diff --git a/src/version.c b/src/version.c index 616f4ede85..b2de0115a2 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2351, /**/ 2350, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 938fc2e191..9c9c273650 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2605,6 +2605,17 @@ call_def_function( break; case ISN_THROW: + if (ectx.ec_trystack.ga_len == 0 && trylevel == 0 + && emsg_silent) + { + // throwing an exception while using "silent!" causes the + // function to abort but not display an error. + tv = STACK_TV_BOT(-1); + clear_tv(tv); + tv->v_type = VAR_NUMBER; + tv->vval.v_number = 0; + goto done; + } --ectx.ec_stack.ga_len; tv = STACK_TV_BOT(0); if (tv->vval.v_string == NULL From d44cc593cee84fc3c712ca1828d1574f80700383 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Thu, 14 Jan 2021 21:57:58 +0100 Subject: [PATCH 26/40] patch 8.2.2352: if focus lost/gained is received twice code is not ignored Problem: If the focus lost/gained escape sequence is received twice it is not ignored. (Christ van Willigen) Solution: Adjust the logic to ignore the escape code. --- src/term.c | 22 ++++++++++++++-------- src/version.c | 2 ++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/term.c b/src/term.c index d3d0581d66..b17bc397e3 100644 --- a/src/term.c +++ b/src/term.c @@ -5708,18 +5708,24 @@ check_termcode( && key_name[0] == KS_EXTRA ) { - if (key_name[1] == KE_FOCUSGAINED && !focus_state) + if (key_name[1] == KE_FOCUSGAINED) { - ui_focus_change(TRUE); - did_cursorhold = TRUE; - focus_state = TRUE; + if (!focus_state) + { + ui_focus_change(TRUE); + did_cursorhold = TRUE; + focus_state = TRUE; + } key_name[1] = (int)KE_IGNORE; } - else if (key_name[1] == KE_FOCUSLOST && focus_state) + else if (key_name[1] == KE_FOCUSLOST) { - ui_focus_change(FALSE); - did_cursorhold = TRUE; - focus_state = FALSE; + if (focus_state) + { + ui_focus_change(FALSE); + did_cursorhold = TRUE; + focus_state = FALSE; + } key_name[1] = (int)KE_IGNORE; } } diff --git a/src/version.c b/src/version.c index b2de0115a2..acca7aab56 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2352, /**/ 2351, /**/ From 17d015b2438e51d4d42d72720611d16c772cc4bb Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 15 Jan 2021 13:35:30 +0100 Subject: [PATCH 27/40] patch 8.2.2353: spartql files are not detected Problem: Spartql files are not detected. Solution: Add the sparql filetype. (closes #7679) --- runtime/filetype.vim | 3 +++ src/testdir/test_filetype.vim | 1 + src/version.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 7a33f8fa2b..4c419ed060 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1618,6 +1618,9 @@ au BufNewFile,BufRead *.mib,*.my setf mib au BufNewFile,BufRead *.hog,snort.conf,vision.conf setf hog au BufNewFile,BufRead *.rules call dist#ft#FTRules() +" SPARQL queries +au BufNewFile,BufRead *.rq,*.sparql setf sparql + " Spec (Linux RPM) au BufNewFile,BufRead *.spec setf spec diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 2c649285db..3d93ad1598 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -445,6 +445,7 @@ let s:filename_checks = { \ 'smith': ['file.smt', 'file.smith'], \ 'sml': ['file.sml'], \ 'snobol4': ['file.sno', 'file.spt'], + \ 'sparql': ['file.rq', 'file.sparql'], \ 'spec': ['file.spec'], \ 'spice': ['file.sp', 'file.spice'], \ 'spup': ['file.speedup', 'file.spdata', 'file.spd'], diff --git a/src/version.c b/src/version.c index acca7aab56..8372920dfd 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2353, /**/ 2352, /**/ From 797e63b9f2baa1853e7063aac478d663cd02f207 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 15 Jan 2021 16:22:52 +0100 Subject: [PATCH 28/40] patch 8.2.2354: crash with a weird combination of autocommands Problem: Crash with a weird combination of autocommands. Solution: Increment b_nwindows when needed. (closes #7674) --- src/buffer.c | 21 ++++++++++++--------- src/ex_cmds.c | 12 +++++++++++- src/proto/buffer.pro | 2 +- src/testdir/test_autocmd.vim | 20 ++++++++++++++++++++ src/version.c | 2 ++ 5 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index c14aefb0e3..c347ef9a64 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -492,8 +492,10 @@ can_unload_buffer(buf_T *buf) * supposed to close the window but autocommands close all other windows. * * When "ignore_abort" is TRUE don't abort even when aborting() returns TRUE. + * + * Return TRUE when we got to the end and b_nwindows was decremented. */ - void + int close_buffer( win_T *win, // if not NULL, set b_last_cursor buf_T *buf, @@ -540,7 +542,7 @@ close_buffer( if (wipe_buf || unload_buf) { if (!can_unload_buffer(buf)) - return; + return FALSE; // Wiping out or unloading a terminal buffer kills the job. free_terminal(buf); @@ -571,7 +573,7 @@ close_buffer( // Disallow deleting the buffer when it is locked (already being closed or // halfway a command that relies on it). Unloading is allowed. if ((del_buf || wipe_buf) && !can_unload_buffer(buf)) - return; + return FALSE; // check no autocommands closed the window if (win != NULL && win_valid_any_tab(win)) @@ -600,7 +602,7 @@ close_buffer( // Autocommands deleted the buffer. aucmd_abort: emsg(_(e_auabort)); - return; + return FALSE; } --buf->b_locked; if (abort_if_last && one_window()) @@ -625,7 +627,7 @@ aucmd_abort: #ifdef FEAT_EVAL // autocmds may abort script processing if (!ignore_abort && aborting()) - return; + return FALSE; #endif } @@ -653,7 +655,7 @@ aucmd_abort: // Return when a window is displaying the buffer or when it's not // unloaded. if (buf->b_nwindows > 0 || !unload_buf) - return; + return FALSE; // Always remove the buffer when there is no file name. if (buf->b_ffname == NULL) @@ -683,11 +685,11 @@ aucmd_abort: // Autocommands may have deleted the buffer. if (!bufref_valid(&bufref)) - return; + return FALSE; #ifdef FEAT_EVAL // autocmds may abort script processing if (!ignore_abort && aborting()) - return; + return FALSE; #endif /* @@ -698,7 +700,7 @@ aucmd_abort: * deleted buffer. */ if (buf == curbuf && !is_curbuf) - return; + return FALSE; if (win_valid_any_tab(win) && win->w_buffer == buf) win->w_buffer = NULL; // make sure we don't use the buffer now @@ -755,6 +757,7 @@ aucmd_abort: buf->b_p_bl = FALSE; } // NOTE: at this point "curbuf" may be invalid! + return TRUE; } /* diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 24763d94d3..a3226d25cf 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -2742,6 +2742,8 @@ do_ecmd( else { win_T *the_curwin = curwin; + int did_decrement; + buf_T *was_curbuf = curbuf; // Set the w_closing flag to avoid that autocommands close the // window. And set b_locked for the same reason. @@ -2754,7 +2756,7 @@ do_ecmd( // Close the link to the current buffer. This will set // oldwin->w_buffer to NULL. u_sync(FALSE); - close_buffer(oldwin, curbuf, + did_decrement = close_buffer(oldwin, curbuf, (flags & ECMD_HIDE) ? 0 : DOBUF_UNLOAD, FALSE, FALSE); the_curwin->w_closing = FALSE; @@ -2776,7 +2778,15 @@ do_ecmd( goto theend; } if (buf == curbuf) // already in new buffer + { + // close_buffer() has decremented the window count, + // increment it again here and restore w_buffer. + if (did_decrement && buf_valid(was_curbuf)) + ++was_curbuf->b_nwindows; + if (win_valid_any_tab(oldwin) && oldwin->w_buffer == NULL) + oldwin->w_buffer = was_curbuf; auto_buf = TRUE; + } else { #ifdef FEAT_SYN_HL diff --git a/src/proto/buffer.pro b/src/proto/buffer.pro index 82bcaf3c1f..564b655402 100644 --- a/src/proto/buffer.pro +++ b/src/proto/buffer.pro @@ -5,7 +5,7 @@ int open_buffer(int read_stdin, exarg_T *eap, int flags); void set_bufref(bufref_T *bufref, buf_T *buf); int bufref_valid(bufref_T *bufref); int buf_valid(buf_T *buf); -void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last, int ignore_abort); +int close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last, int ignore_abort); void buf_clear_file(buf_T *buf); void buf_freeall(buf_T *buf, int flags); void free_wininfo(wininfo_T *wip); diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 97b091466f..0d6b414e68 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -500,6 +500,26 @@ func Test_autocmd_bufwipe_in_SessLoadPost() endfor endfunc +" Using :blast and :ball for many events caused a crash, because b_nwindows was +" not incremented correctly. +func Test_autocmd_blast_badd() + let content =<< trim [CODE] + au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* blast + edit foo1 + au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* ball + edit foo2 + call writefile(['OK'], 'Xerrors') + qall + [CODE] + + call writefile(content, 'XblastBall') + call system(GetVimCommand() .. ' --clean -S XblastBall') + call assert_match('OK', readfile('Xerrors')->join()) + + call delete('XblastBall') + call delete('Xerrors') +endfunc + " SEGV occurs in older versions. func Test_autocmd_bufwipe_in_SessLoadPost2() tabnew diff --git a/src/version.c b/src/version.c index 8372920dfd..e6113c91b0 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2354, /**/ 2353, /**/ From 97c6943e11516711541848e51db3cc2ace25bbb2 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 15 Jan 2021 16:45:21 +0100 Subject: [PATCH 29/40] patch 8.2.2355: stray test failure on Appveyor Problem: Stray test failure on Appveyor. Solution: Finish insert command. --- src/testdir/test_autocmd.vim | 6 +++--- src/version.c | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 0d6b414e68..6f8c973d7f 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -1639,14 +1639,14 @@ func Test_BufReadCmd() endfunc func SetChangeMarks(start, end) - exe a:start. 'mark [' - exe a:end. 'mark ]' + exe a:start .. 'mark [' + exe a:end .. 'mark ]' endfunc " Verify the effects of autocmds on '[ and '] func Test_change_mark_in_autocmds() edit! Xtest - call feedkeys("ia\b\c\d\u", 'xtn') + call feedkeys("ia\b\c\d\u\", 'xtn') call SetChangeMarks(2, 3) write diff --git a/src/version.c b/src/version.c index e6113c91b0..8285df7721 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2355, /**/ 2354, /**/ From 883cf97f109d2ff281cf77f7b2e3bb44aced7cb3 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 15 Jan 2021 18:04:43 +0100 Subject: [PATCH 30/40] patch 8.2.2356: Vim9: ":put =expr" does not handle a list properly Problem: Vim9: ":put =expr" does not handle a list properly. Solution: Use the same logic as eval_to_string_eap(). (closes #7684) --- src/eval.c | 65 +++++++++++++++++++++-------------- src/proto/eval.pro | 1 + src/testdir/test_vim9_cmd.vim | 3 ++ src/version.c | 2 ++ src/vim9execute.c | 2 +- 5 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/eval.c b/src/eval.c index 84f6c2bf27..13ee49aac4 100644 --- a/src/eval.c +++ b/src/eval.c @@ -466,6 +466,45 @@ skip_expr_concatenate( return res; } +/* + * Convert "tv" to a string. + * When "convert" is TRUE convert a List into a sequence of lines and convert + * a Float to a String. + * Returns an allocated string (NULL when out of memory). + */ + char_u * +typval2string(typval_T *tv, int convert) +{ + garray_T ga; + char_u *retval; +#ifdef FEAT_FLOAT + char_u numbuf[NUMBUFLEN]; +#endif + + if (convert && tv->v_type == VAR_LIST) + { + ga_init2(&ga, (int)sizeof(char), 80); + if (tv->vval.v_list != NULL) + { + list_join(&ga, tv->vval.v_list, (char_u *)"\n", TRUE, FALSE, 0); + if (tv->vval.v_list->lv_len > 0) + ga_append(&ga, NL); + } + ga_append(&ga, NUL); + retval = (char_u *)ga.ga_data; + } +#ifdef FEAT_FLOAT + else if (convert && tv->v_type == VAR_FLOAT) + { + vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv->vval.v_float); + retval = vim_strsave(numbuf); + } +#endif + else + retval = vim_strsave(tv_get_string(tv)); + return retval; +} + /* * Top level evaluation function, returning a string. Does not handle line * breaks. @@ -481,10 +520,6 @@ eval_to_string_eap( { typval_T tv; char_u *retval; - garray_T ga; -#ifdef FEAT_FLOAT - char_u numbuf[NUMBUFLEN]; -#endif evalarg_T evalarg; fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip); @@ -492,27 +527,7 @@ eval_to_string_eap( retval = NULL; else { - if (convert && tv.v_type == VAR_LIST) - { - ga_init2(&ga, (int)sizeof(char), 80); - if (tv.vval.v_list != NULL) - { - list_join(&ga, tv.vval.v_list, (char_u *)"\n", TRUE, FALSE, 0); - if (tv.vval.v_list->lv_len > 0) - ga_append(&ga, NL); - } - ga_append(&ga, NUL); - retval = (char_u *)ga.ga_data; - } -#ifdef FEAT_FLOAT - else if (convert && tv.v_type == VAR_FLOAT) - { - vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", tv.vval.v_float); - retval = vim_strsave(numbuf); - } -#endif - else - retval = vim_strsave(tv_get_string(&tv)); + retval = typval2string(&tv, convert); clear_tv(&tv); } clear_evalarg(&evalarg, NULL); diff --git a/src/proto/eval.pro b/src/proto/eval.pro index f611a0efa7..fbae530306 100644 --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -11,6 +11,7 @@ int eval_expr_to_bool(typval_T *expr, int *error); char_u *eval_to_string_skip(char_u *arg, exarg_T *eap, int skip); int skip_expr(char_u **pp, evalarg_T *evalarg); int skip_expr_concatenate(char_u **arg, char_u **start, char_u **end, evalarg_T *evalarg); +char_u *typval2string(typval_T *tv, int convert); char_u *eval_to_string_eap(char_u *arg, int convert, exarg_T *eap); char_u *eval_to_string(char_u *arg, int convert); char_u *eval_to_string_safe(char_u *arg, int use_sandbox); diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 5d66966801..098cded98a 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -736,6 +736,9 @@ def Test_put_command() assert_equal('above', getline(3)) assert_equal('below', getline(4)) + :2put =['a', 'b', 'c'] + assert_equal(['ppp', 'a', 'b', 'c', 'above'], getline(2, 6)) + # compute range at runtime setline(1, range(1, 8)) @a = 'aaa' diff --git a/src/version.c b/src/version.c index 8285df7721..9d217e2a06 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2356, /**/ 2355, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 9c9c273650..87a9218ae4 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -3357,7 +3357,7 @@ call_def_function( expr = tv->vval.v_string; else { - expr = typval_tostring(tv); // allocates value + expr = typval2string(tv, TRUE); // allocates value clear_tv(tv); } --ectx.ec_stack.ga_len; From 648ea76e1d8ca9a9788f88d0d96d0cf0653006bb Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Fri, 15 Jan 2021 19:04:32 +0100 Subject: [PATCH 31/40] patch 8.2.2357: Vim9: crash when parsing function return type fails Problem: Vim9: crash when parsing function return type fails. Solution: Bail out and set return type to "unknown". (closes #7685) --- src/testdir/test_vim9_func.vim | 11 +++++++++++ src/userfunc.c | 6 ++++++ src/version.c | 2 ++ 3 files changed, 19 insertions(+) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index e87c33e909..049532c69f 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -197,6 +197,17 @@ def Test_return_nothing() s:nothing->assert_equal(1) enddef +def Test_return_invalid() + var lines =<< trim END + vim9script + def Func(): invalid + return xxx + enddef + defcompile + END + CheckScriptFailure(lines, 'E1010:', 2) +enddef + func Increment() let g:counter += 1 endfunc diff --git a/src/userfunc.c b/src/userfunc.c index ded9ef7b0f..7ca8186e13 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3886,6 +3886,12 @@ define_function(exarg_T *eap, char_u *name_arg) { p = ret_type; fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE); + if (fp->uf_ret_type == NULL) + { + fp->uf_ret_type = &t_void; + SOURCING_LNUM = lnum_save; + goto erret; + } } SOURCING_LNUM = lnum_save; } diff --git a/src/version.c b/src/version.c index 9d217e2a06..e7417402fb 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2357, /**/ 2356, /**/ From bf78974ca4a6798db7172c226cbfe7b2485b7baa Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 16 Jan 2021 11:21:40 +0100 Subject: [PATCH 32/40] patch 8.2.2358: wrong #ifdef for use_xterm_like_mouse() Problem: Wrong #ifdef for use_xterm_like_mouse(). Solution: Use FEAT_MOUSE_XTERM. --- src/term.c | 10 +++++----- src/version.c | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/term.c b/src/term.c index b17bc397e3..5d1228dd7d 100644 --- a/src/term.c +++ b/src/term.c @@ -2053,20 +2053,20 @@ set_termname(char_u *term) set_mouse_termcode(KS_MOUSE, (char_u *)"\233M"); #endif -#if (defined(UNIX) || defined(VMS)) +#ifdef FEAT_MOUSE_XTERM // focus reporting is supported by xterm compatible terminals and tmux. if (use_xterm_like_mouse(term)) { char_u name[3]; - name[0] = (int)KS_EXTRA; - name[2] = NUL; // handle focus in event - name[1] = (int)KE_FOCUSGAINED; + name[0] = KS_EXTRA; + name[1] = KE_FOCUSGAINED; + name[2] = NUL; add_termcode(name, (char_u *)"\033[I", FALSE); // handle focus out event - name[1] = (int)KE_FOCUSLOST; + name[1] = KE_FOCUSLOST; add_termcode(name, (char_u *)"\033[O", FALSE); focus_mode = TRUE; diff --git a/src/version.c b/src/version.c index e7417402fb..3603bf4b6f 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2358, /**/ 2357, /**/ From e2924328c165f1fc549f91bf212c93c87bb1d9ed Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 16 Jan 2021 13:11:42 +0100 Subject: [PATCH 33/40] patch 8.2.2359: strange test failure with MS-Windows Problem: Strange test failure with MS-Windows. Solution: Skip the system() call for now. --- src/testdir/test_autocmd.vim | 8 ++++++++ src/version.c | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 6f8c973d7f..5ec854e676 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -503,6 +503,14 @@ endfunc " Using :blast and :ball for many events caused a crash, because b_nwindows was " not incremented correctly. func Test_autocmd_blast_badd() + " The system() here causes SetChangeMarks() to fail, when run in the GUI + " under Windows. No idea why. Happens with any external command, not + " related to the actual test. + " TODO: find the cause + if has('win32') + throw 'Skipped: calling system() causes problems' + endif + let content =<< trim [CODE] au BufNew,BufAdd,BufWinEnter,BufEnter,BufLeave,BufWinLeave,BufUnload,VimEnter foo* blast edit foo1 diff --git a/src/version.c b/src/version.c index 3603bf4b6f..51f8f0b47d 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2359, /**/ 2358, /**/ From a3b494d6afa79aedce42fa4ecc7ef0dbae79e37e Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 16 Jan 2021 13:43:31 +0100 Subject: [PATCH 34/40] patch 8.2.2360: test leaves file behind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Test leaves file behind. Solution: Delete the right file. (Dominique Pellé, closes #7689) --- src/testdir/test_filetype.vim | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 3d93ad1598..b05c152dc6 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -757,7 +757,7 @@ func Test_pp_file() call assert_equal('pascal', &filetype) bwipe! - call delete('Xfile.ts') + call delete('Xfile.pp') filetype off endfunc diff --git a/src/version.c b/src/version.c index 51f8f0b47d..aa86cab495 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2360, /**/ 2359, /**/ From 7c886db915035bc064ca307f02c34ae9d99cc733 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 16 Jan 2021 14:34:45 +0100 Subject: [PATCH 35/40] patch 8.2.2361: Vim9: no highlight for "s///gc" when using 'opfunc' Problem: Vim9: no highlight for "s///gc" when using 'opfunc'. Solution: Reset 'lazyredraw' temporarily. (closes #7687) --- src/ex_cmds.c | 5 +++++ src/version.c | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/ex_cmds.c b/src/ex_cmds.c index a3226d25cf..545e06dbde 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -4158,6 +4158,7 @@ ex_substitute(exarg_T *eap) { char_u *orig_line = NULL; int len_change = 0; + int save_p_lz = p_lz; #ifdef FEAT_FOLDING int save_p_fen = curwin->w_p_fen; @@ -4168,6 +4169,9 @@ ex_substitute(exarg_T *eap) temp = RedrawingDisabled; RedrawingDisabled = 0; + // avoid calling update_screen() in vgetorpeek() + p_lz = FALSE; + if (new_start != NULL) { // There already was a substitution, we would @@ -4243,6 +4247,7 @@ ex_substitute(exarg_T *eap) msg_didout = FALSE; // don't scroll up msg_col = 0; gotocmdline(TRUE); + p_lz = save_p_lz; // restore the line if (orig_line != NULL) diff --git a/src/version.c b/src/version.c index aa86cab495..8dccaf280e 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2361, /**/ 2360, /**/ From 351ead09dd365ebdee2bfa27ab22542d4920c779 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 16 Jan 2021 16:07:01 +0100 Subject: [PATCH 36/40] patch 8.2.2362: Vim9: check of builtin function argument type is incomplete Problem: Vim9: check of builtin function argument type is incomplete. Solution: Use need_type() instead of check_arg_type(). --- src/evalfunc.c | 34 +++++++++++++++++++----- src/proto/evalfunc.pro | 2 +- src/proto/vim9compile.pro | 1 + src/proto/vim9type.pro | 1 - src/testdir/test_vim9_builtin.vim | 3 +++ src/version.c | 2 ++ src/vim9compile.c | 44 ++++++++++++++++--------------- src/vim9type.c | 20 ++++---------- 8 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 0e800c2b6f..61cca01f8a 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -275,6 +275,7 @@ typedef struct { int arg_count; // actual argument count type_T **arg_types; // list of argument types int arg_idx; // current argument index (first arg is zero) + cctx_T *arg_cctx; } argcontext_T; // A function to check one argument type. The first argument is the type to @@ -282,6 +283,22 @@ typedef struct { // E.g. if "arg_idx" is 1, then (type - 1) is the first argument type. typedef int (*argcheck_T)(type_T *, argcontext_T *); +/* + * Call need_type() to check an argument type. + */ + static int +check_arg_type( + type_T *expected, + type_T *actual, + argcontext_T *context) +{ + // TODO: would be useful to know if "actual" is a constant and pass it to + // need_type() to get a compile time error if possible. + return need_type(actual, expected, + context->arg_idx - context->arg_count, context->arg_idx + 1, + context->arg_cctx, FALSE, FALSE); +} + /* * Check "type" is a float or a number. */ @@ -301,7 +318,7 @@ arg_float_or_nr(type_T *type, argcontext_T *context) static int arg_number(type_T *type, argcontext_T *context) { - return check_arg_type(&t_number, type, context->arg_idx + 1); + return check_arg_type(&t_number, type, context); } /* @@ -310,7 +327,7 @@ arg_number(type_T *type, argcontext_T *context) static int arg_string(type_T *type, argcontext_T *context) { - return check_arg_type(&t_string, type, context->arg_idx + 1); + return check_arg_type(&t_string, type, context); } /* @@ -348,7 +365,7 @@ arg_same_as_prev(type_T *type, argcontext_T *context) { type_T *prev_type = context->arg_types[context->arg_idx - 1]; - return check_arg_type(prev_type, type, context->arg_idx + 1); + return check_arg_type(prev_type, type, context); } /* @@ -362,7 +379,7 @@ arg_same_struct_as_prev(type_T *type, argcontext_T *context) type_T *prev_type = context->arg_types[context->arg_idx - 1]; if (prev_type->tt_type != context->arg_types[context->arg_idx]->tt_type) - return check_arg_type(prev_type, type, context->arg_idx + 1); + return check_arg_type(prev_type, type, context); return OK; } @@ -384,7 +401,7 @@ arg_item_of_prev(type_T *type, argcontext_T *context) // probably VAR_ANY, can't check return OK; - return check_arg_type(expected, type, context->arg_idx + 1); + return check_arg_type(expected, type, context); } /* @@ -1931,7 +1948,11 @@ internal_func_name(int idx) * Return FAIL and gives an error message when a type is wrong. */ int -internal_func_check_arg_types(type_T **types, int idx, int argcount) +internal_func_check_arg_types( + type_T **types, + int idx, + int argcount, + cctx_T *cctx) { argcheck_T *argchecks = global_functions[idx].f_argcheck; int i; @@ -1942,6 +1963,7 @@ internal_func_check_arg_types(type_T **types, int idx, int argcount) context.arg_count = argcount; context.arg_types = types; + context.arg_cctx = cctx; for (i = 0; i < argcount; ++i) if (argchecks[i] != NULL) { diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro index ad4b98a29d..13dffacfff 100644 --- a/src/proto/evalfunc.pro +++ b/src/proto/evalfunc.pro @@ -4,7 +4,7 @@ char_u *get_expr_name(expand_T *xp, int idx); int find_internal_func(char_u *name); int has_internal_func(char_u *name); char *internal_func_name(int idx); -int internal_func_check_arg_types(type_T **types, int idx, int argcount); +int internal_func_check_arg_types(type_T **types, int idx, int argcount, cctx_T *cctx); type_T *internal_func_ret_type(int idx, int argcount, type_T **argtypes); int internal_func_is_map(int idx); int check_internal_func(int idx, int argcount); diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro index 7800d3ba59..d1d1b0c7d0 100644 --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -2,6 +2,7 @@ int check_defined(char_u *p, size_t len, cctx_T *cctx); int check_compare_types(exprtype_T type, typval_T *tv1, typval_T *tv2); int use_typecheck(type_T *actual, type_T *expected); +int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const); int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx); imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx); imported_T *find_imported_in_script(char_u *name, size_t len, int sid); diff --git a/src/proto/vim9type.pro b/src/proto/vim9type.pro index 316c650115..f7b472c9e6 100644 --- a/src/proto/vim9type.pro +++ b/src/proto/vim9type.pro @@ -15,7 +15,6 @@ int check_typval_type(type_T *expected, typval_T *actual_tv, int argidx); void type_mismatch(type_T *expected, type_T *actual); void arg_type_mismatch(type_T *expected, type_T *actual, int argidx); int check_type(type_T *expected, type_T *actual, int give_msg, int argidx); -int check_arg_type(type_T *expected, type_T *actual, int argidx); int check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name); char_u *skip_type(char_u *start, int optional); type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error); diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index f1c5de14e0..180070e0aa 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -241,6 +241,9 @@ def Test_extend_arg_types() CheckDefFailure(['extend({a: 1}, 42)'], 'E1013: Argument 2: type mismatch, expected dict but got number') CheckDefFailure(['extend({a: 1}, {b: "x"})'], 'E1013: Argument 2: type mismatch, expected dict but got dict') CheckDefFailure(['extend({a: 1}, {b: 2}, 1)'], 'E1013: Argument 3: type mismatch, expected string but got number') + + CheckDefFailure(['extend([1], ["b"])'], 'E1013: Argument 2: type mismatch, expected list but got list') + CheckDefExecFailure(['extend([1], ["b", 1])'], 'E1012: Type mismatch; expected list but got list') enddef def Test_extendnew() diff --git a/src/version.c b/src/version.c index 8dccaf280e..73bdc24dc5 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2362, /**/ 2361, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index b0b5973dd5..fb7aeddd49 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -878,11 +878,12 @@ use_typecheck(type_T *actual, type_T *expected) * If "actual_is_const" is TRUE then the type won't change at runtime, do not * generate a TYPECHECK. */ - static int + int need_type( type_T *actual, type_T *expected, int offset, + int arg_idx, cctx_T *cctx, int silent, int actual_is_const) @@ -896,7 +897,7 @@ need_type( return OK; } - if (check_type(expected, actual, FALSE, 0) == OK) + if (check_type(expected, actual, FALSE, arg_idx) == OK) return OK; // If the actual type can be the expected type add a runtime check. @@ -908,7 +909,7 @@ need_type( } if (!silent) - type_mismatch(expected, actual); + arg_type_mismatch(expected, actual, arg_idx); return FAIL; } @@ -931,7 +932,7 @@ bool_on_stack(cctx_T *cctx) // This requires a runtime type check. return generate_COND2BOOL(cctx); - return need_type(type, &t_bool, -1, cctx, FALSE, FALSE); + return need_type(type, &t_bool, -1, 0, cctx, FALSE, FALSE); } /* @@ -1613,7 +1614,8 @@ generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call) { // Check the types of the arguments. argtypes = ((type_T **)stack->ga_data) + stack->ga_len - argcount; - if (internal_func_check_arg_types(argtypes, func_idx, argcount) == FAIL) + if (internal_func_check_arg_types(argtypes, func_idx, argcount, + cctx) == FAIL) return FAIL; if (internal_func_is_map(func_idx)) maptype = *argtypes; @@ -1656,7 +1658,7 @@ generate_LISTAPPEND(cctx_T *cctx) list_type = ((type_T **)stack->ga_data)[stack->ga_len - 2]; item_type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; expected = list_type->tt_member; - if (need_type(item_type, expected, -1, cctx, FALSE, FALSE) == FAIL) + if (need_type(item_type, expected, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; if (generate_instr(cctx, ISN_LISTAPPEND) == NULL) @@ -1678,7 +1680,7 @@ generate_BLOBAPPEND(cctx_T *cctx) // Caller already checked that blob_type is a blob. item_type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (need_type(item_type, &t_number, -1, cctx, FALSE, FALSE) == FAIL) + if (need_type(item_type, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; if (generate_instr(cctx, ISN_BLOBAPPEND) == NULL) @@ -1733,7 +1735,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount) else expected = ufunc->uf_va_type->tt_member; actual = ((type_T **)stack->ga_data)[stack->ga_len - argcount + i]; - if (need_type(actual, expected, -argcount + i, cctx, + if (need_type(actual, expected, -argcount + i, 0, cctx, TRUE, FALSE) == FAIL) { arg_type_mismatch(expected, actual, i + 1); @@ -1850,7 +1852,7 @@ generate_PCALL( type->tt_argcount - 1]->tt_member; else expected = type->tt_args[i]; - if (need_type(actual, expected, offset, + if (need_type(actual, expected, offset, 0, cctx, TRUE, FALSE) == FAIL) { arg_type_mismatch(expected, actual, i + 1); @@ -3135,7 +3137,7 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) { type_T *keytype = ((type_T **)stack->ga_data) [stack->ga_len - 1]; - if (need_type(keytype, &t_string, -1, cctx, + if (need_type(keytype, &t_string, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } @@ -3808,13 +3810,13 @@ compile_subscript( vtype = VAR_DICT; if (vtype == VAR_STRING || vtype == VAR_LIST || vtype == VAR_BLOB) { - if (need_type(valtype, &t_number, -1, cctx, + if (need_type(valtype, &t_number, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; if (is_slice) { valtype = ((type_T **)stack->ga_data)[stack->ga_len - 2]; - if (need_type(valtype, &t_number, -2, cctx, + if (need_type(valtype, &t_number, -2, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } @@ -3836,7 +3838,7 @@ compile_subscript( } else { - if (need_type(*typep, &t_dict_any, -2, cctx, + if (need_type(*typep, &t_dict_any, -2, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; *typep = &t_any; @@ -4235,7 +4237,7 @@ compile_expr7t(char_u **arg, cctx_T *cctx, ppconst_T *ppconst) actual = ((type_T **)stack->ga_data)[stack->ga_len - 1]; if (check_type(want_type, actual, FALSE, 0) == FAIL) { - if (need_type(actual, want_type, -1, cctx, FALSE, FALSE) == FAIL) + if (need_type(actual, want_type, -1, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } } @@ -4917,7 +4919,7 @@ compile_return(char_u *arg, int check_return_type, cctx_T *cctx) return NULL; } if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1, - cctx, FALSE, FALSE) == FAIL) + 0, cctx, FALSE, FALSE) == FAIL) return NULL; } } @@ -5831,7 +5833,7 @@ compile_assign_unlet( : ((type_T **)stack->ga_data)[stack->ga_len - 1]; // now we can properly check the type if (lhs->lhs_type->tt_member != NULL && rhs_type != &t_void - && need_type(rhs_type, lhs->lhs_type->tt_member, -2, cctx, + && need_type(rhs_type, lhs->lhs_type->tt_member, -2, 0, cctx, FALSE, FALSE) == FAIL) return FAIL; } @@ -5976,7 +5978,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) emsg(_(e_cannot_use_void_value)); goto theend; } - if (need_type(stacktype, &t_list_any, -1, cctx, + if (need_type(stacktype, &t_list_any, -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; // TODO: check the length of a constant list here @@ -6123,13 +6125,13 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) // without operator check type here, otherwise below if (lhs.lhs_has_index) use_type = lhs.lhs_member_type; - if (need_type(rhs_type, use_type, -1, cctx, + if (need_type(rhs_type, use_type, -1, 0, cctx, FALSE, is_const) == FAIL) goto theend; } } else if (*p != '=' && need_type(rhs_type, lhs.lhs_member_type, - -1, cctx, FALSE, FALSE) == FAIL) + -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; } else if (cmdidx == CMD_final) @@ -6216,7 +6218,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) // If variable is float operation with number is OK. !(expected == &t_float && stacktype == &t_number) && #endif - need_type(stacktype, expected, -1, cctx, + need_type(stacktype, expected, -1, 0, cctx, FALSE, FALSE) == FAIL) goto theend; @@ -6925,7 +6927,7 @@ compile_for(char_u *arg_start, cctx_T *cctx) // Now that we know the type of "var", check that it is a list, now or at // runtime. vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; - if (need_type(vartype, &t_list_any, -1, cctx, FALSE, FALSE) == FAIL) + if (need_type(vartype, &t_list_any, -1, 0, cctx, FALSE, FALSE) == FAIL) { drop_scope(cctx); return NULL; diff --git a/src/vim9type.c b/src/vim9type.c index 85f9bd8347..81b0db22df 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -513,26 +513,16 @@ check_type(type_T *expected, type_T *actual, int give_msg, int argidx) return ret; } -/* - * Like check_type() but also allow for a runtime type check. E.g. "any" can be - * used for "number". - */ - int -check_arg_type(type_T *expected, type_T *actual, int argidx) -{ - if (check_type(expected, actual, FALSE, 0) == OK - || use_typecheck(actual, expected)) - return OK; - // TODO: should generate a TYPECHECK instruction. - return check_type(expected, actual, TRUE, argidx); -} - /* * Check that the arguments of "type" match "argvars[argcount]". * Return OK/FAIL. */ int -check_argument_types(type_T *type, typval_T *argvars, int argcount, char_u *name) +check_argument_types( + type_T *type, + typval_T *argvars, + int argcount, + char_u *name) { int varargs = (type->tt_flags & TTFLAG_VARARGS) ? 1 : 0; int i; From 9ebcf231bdccc1673cc92b20f5190fc577ad29d0 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 16 Jan 2021 16:52:49 +0100 Subject: [PATCH 37/40] patch 8.2.2363: curpos() does not accept a string argument as before Problem: curpos() does not accept a string argument as before. solution: Make a string argument work again. (Yegappan Lakshmanan, closes #7690 --- src/evalfunc.c | 3 ++- src/testdir/test_cursor_func.vim | 3 +++ src/version.c | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index 61cca01f8a..c9acd4136b 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2767,7 +2767,8 @@ set_cursorpos(typval_T *argvars, typval_T *rettv, int charcol) } else if ((argvars[0].v_type == VAR_NUMBER || argvars[0].v_type == VAR_STRING) - && argvars[1].v_type == VAR_NUMBER) + && (argvars[1].v_type == VAR_NUMBER || + argvars[1].v_type == VAR_STRING)) { line = tv_get_lnum(argvars); if (line < 0) diff --git a/src/testdir/test_cursor_func.vim b/src/testdir/test_cursor_func.vim index c09c6837e2..bded15e36a 100644 --- a/src/testdir/test_cursor_func.vim +++ b/src/testdir/test_cursor_func.vim @@ -25,6 +25,9 @@ func Test_move_cursor() " below last line goes to last line eval [9, 1]->cursor() call assert_equal([4, 1, 0, 1], getcurpos()[1:]) + " pass string arguments + call cursor('3', '3') + call assert_equal([3, 3, 0, 3], getcurpos()[1:]) call setline(1, ["\"]) call cursor(1, 1, 1) diff --git a/src/version.c b/src/version.c index 73bdc24dc5..81ce6bc295 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2363, /**/ 2362, /**/ From f898f7c68dc06def1a5ae02ce82a52a82af37cc4 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 16 Jan 2021 18:09:52 +0100 Subject: [PATCH 38/40] patch 8.2.2364: Vim9: line break in lambda accesses freed memory Problem: Vim9: line break in lambda accesses freed memory. Solution: Make a copy of the return type. (closes #7664) --- src/testdir/test_vim9_func.vim | 12 ++++++++++++ src/userfunc.c | 22 ++++++++++++++++------ src/version.c | 2 ++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 049532c69f..1ea8ac9fc9 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -1811,6 +1811,18 @@ enddef def Test_line_continuation_in_lambda() Line_continuation_in_lambda()->assert_equal(['D', 'C', 'B', 'A']) + + var lines =<< trim END + vim9script + var res = [{n: 1, m: 2, s: 'xxx'}] + ->mapnew((_, v: dict): string => printf('%d:%d:%s', + v.n, + v.m, + substitute(v.s, '.*', 'yyy', '') + )) + assert_equal(['1:2:yyy'], res) + END + CheckScriptSuccess(lines) enddef def Test_list_lambda() diff --git a/src/userfunc.c b/src/userfunc.c index 7ca8186e13..bf701b42eb 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -539,7 +539,8 @@ get_lambda_tv( char_u *start, *end; int *old_eval_lavars = eval_lavars_used; int eval_lavars = FALSE; - char_u *tofree = NULL; + char_u *tofree1 = NULL; + char_u *tofree2 = NULL; int equal_arrow = **arg == '('; int white_error = FALSE; @@ -582,6 +583,13 @@ get_lambda_tv( } *arg = s; + // Skipping over linebreaks may make "ret_type" invalid, make a copy. + if (ret_type != NULL) + { + ret_type = vim_strsave(ret_type); + tofree2 = ret_type; + } + // Set up a flag for checking local variables and arguments. if (evaluate) eval_lavars_used = &eval_lavars; @@ -605,7 +613,7 @@ get_lambda_tv( if (evalarg != NULL) { // avoid that the expression gets freed when another line break follows - tofree = evalarg->eval_tofree; + tofree1 = evalarg->eval_tofree; evalarg->eval_tofree = NULL; } @@ -700,9 +708,10 @@ get_lambda_tv( eval_lavars_used = old_eval_lavars; if (evalarg != NULL && evalarg->eval_tofree == NULL) - evalarg->eval_tofree = tofree; + evalarg->eval_tofree = tofree1; else - vim_free(tofree); + vim_free(tofree1); + vim_free(tofree2); if (types_optional) ga_clear_strings(&argtypes); return OK; @@ -715,9 +724,10 @@ errret: vim_free(fp); vim_free(pt); if (evalarg != NULL && evalarg->eval_tofree == NULL) - evalarg->eval_tofree = tofree; + evalarg->eval_tofree = tofree1; else - vim_free(tofree); + vim_free(tofree1); + vim_free(tofree2); eval_lavars_used = old_eval_lavars; return FAIL; } diff --git a/src/version.c b/src/version.c index 81ce6bc295..674f2b7bd4 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2364, /**/ 2363, /**/ From 70250fb4d2ffc2e92db224c6374db418f70691fd Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 16 Jan 2021 19:01:53 +0100 Subject: [PATCH 39/40] patch 8.2.2365: Vim9: no check for map() changing item type at script level Problem: Vim9: no check for map() changing item type at script level. Solution: Check the new value type. --- src/list.c | 45 +++++++++++++++++++---- src/testdir/test_vim9_assign.vim | 2 +- src/testdir/test_vim9_builtin.vim | 60 ++++++++++++++++++++----------- src/version.c | 2 ++ 4 files changed, 80 insertions(+), 29 deletions(-) diff --git a/src/list.c b/src/list.c index 0bca0b5530..d6357f789c 100644 --- a/src/list.c +++ b/src/list.c @@ -1985,10 +1985,18 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) : N_("filter() argument")); int save_did_emsg; int idx = 0; + type_T *type = NULL; + garray_T type_list; // map() and filter() return the first argument, also on failure. if (filtermap != FILTERMAP_MAPNEW) copy_tv(&argvars[0], rettv); + if (filtermap == FILTERMAP_MAP && in_vim9script()) + { + // Check that map() does not change the type of the dict. + ga_init2(&type_list, sizeof(type_T *), 10); + type = typval2type(argvars, &type_list); + } if (argvars[0].v_type == VAR_BLOB) { @@ -1998,7 +2006,7 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) rettv->vval.v_blob = NULL; } if ((b = argvars[0].vval.v_blob) == NULL) - return; + goto theend; } else if (argvars[0].v_type == VAR_LIST) { @@ -2010,7 +2018,7 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) if ((l = argvars[0].vval.v_list) == NULL || (filtermap == FILTERMAP_FILTER && value_check_lock(l->lv_lock, arg_errmsg, TRUE))) - return; + goto theend; } else if (argvars[0].v_type == VAR_DICT) { @@ -2022,12 +2030,12 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) if ((d = argvars[0].vval.v_dict) == NULL || (filtermap == FILTERMAP_FILTER && value_check_lock(d->dv_lock, arg_errmsg, TRUE))) - return; + goto theend; } else { semsg(_(e_listdictblobarg), ermsg); - return; + goto theend; } expr = &argvars[1]; @@ -2055,7 +2063,7 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) if (filtermap == FILTERMAP_MAPNEW) { if (rettv_dict_alloc(rettv) == FAIL) - return; + goto theend; d_ret = rettv->vval.v_dict; } @@ -2090,6 +2098,12 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) } if (filtermap == FILTERMAP_MAP) { + if (type != NULL && check_typval_type(type->tt_member, + &newtv, 0) == FAIL) + { + clear_tv(&newtv); + break; + } // map(): replace the dict item value clear_tv(&di->di_tv); newtv.v_lock = 0; @@ -2126,7 +2140,7 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) if (filtermap == FILTERMAP_MAPNEW) { if (blob_copy(b, rettv) == FAIL) - return; + goto theend; b_ret = rettv->vval.v_blob; } @@ -2175,7 +2189,7 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) if (filtermap == FILTERMAP_MAPNEW) { if (rettv_list_alloc(rettv) == FAIL) - return; + goto theend; l_ret = rettv->vval.v_list; } // set_vim_var_nr() doesn't set the type @@ -2218,6 +2232,13 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) } if (filtermap != FILTERMAP_FILTER) { + if (filtermap == FILTERMAP_MAP && type != NULL + && check_typval_type(type->tt_member, + &newtv, 0) == FAIL) + { + clear_tv(&newtv); + break; + } // map(), mapnew(): always append the new value to the // list if (list_append_tv_move(filtermap == FILTERMAP_MAP @@ -2256,6 +2277,12 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) } if (filtermap == FILTERMAP_MAP) { + if (type != NULL && check_typval_type(type->tt_member, + &newtv, 0) == FAIL) + { + clear_tv(&newtv); + break; + } // map(): replace the list item value clear_tv(&li->li_tv); newtv.v_lock = 0; @@ -2281,6 +2308,10 @@ filter_map(typval_T *argvars, typval_T *rettv, filtermap_T filtermap) did_emsg |= save_did_emsg; } + +theend: + if (type != NULL) + clear_type_list(&type_list); } /* diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 8dadb2c07c..c035558803 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -1351,7 +1351,7 @@ def Test_var_list_dict_type() var ll: list ll = [1, 2, 3]->map('"one"') END - CheckDefExecFailure(lines, 'E1012: Type mismatch; expected list but got list') + CheckDefExecFailure(lines, 'E1012: Type mismatch; expected number but got string') enddef def Test_cannot_use_let() diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 180070e0aa..61d129ec23 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -331,27 +331,6 @@ def Wrong_dict_key_type(items: list): list return filter(items, (_, val) => get({[val]: 1}, 'x')) enddef -def Test_map_function_arg() - var lines =<< trim END - def MapOne(i: number, v: string): string - return i .. ':' .. v - enddef - var l = ['a', 'b', 'c'] - map(l, MapOne) - assert_equal(['0:a', '1:b', '2:c'], l) - END - CheckDefAndScriptSuccess(lines) -enddef - -def Test_map_item_type() - var lines =<< trim END - var l = ['a', 'b', 'c'] - map(l, (k, v) => k .. '/' .. v ) - assert_equal(['0/a', '1/b', '2/c'], l) - END - CheckDefAndScriptSuccess(lines) -enddef - def Test_filereadable() assert_false(filereadable("")) assert_false(filereadable(test_null_string())) @@ -584,6 +563,45 @@ def SID(): number ->str2nr() enddef +def Test_map_function_arg() + var lines =<< trim END + def MapOne(i: number, v: string): string + return i .. ':' .. v + enddef + var l = ['a', 'b', 'c'] + map(l, MapOne) + assert_equal(['0:a', '1:b', '2:c'], l) + END + CheckDefAndScriptSuccess(lines) +enddef + +def Test_map_item_type() + var lines =<< trim END + var l = ['a', 'b', 'c'] + map(l, (k, v) => k .. '/' .. v ) + assert_equal(['0/a', '1/b', '2/c'], l) + END + CheckDefAndScriptSuccess(lines) + + lines =<< trim END + var l: list = [0] + echo map(l, (_, v) => []) + END + CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list', 2) + + lines =<< trim END + var l: list = range(2) + echo map(l, (_, v) => []) + END + CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list', 2) + + lines =<< trim END + var d: dict = {key: 0} + echo map(d, (_, v) => []) + END + CheckDefExecAndScriptFailure(lines, 'E1012: Type mismatch; expected number but got list', 2) +enddef + def Test_maparg() var lnum = str2nr(expand('')) map foo bar diff --git a/src/version.c b/src/version.c index 674f2b7bd4..d0010406d5 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2365, /**/ 2364, /**/ From e2edc2ed4a9a229870b1e1811b0ecf045b84e429 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sat, 16 Jan 2021 20:21:23 +0100 Subject: [PATCH 40/40] patch 8.2.2366: when using ":sleep" the cursor is always displayed Problem: When using ":sleep" the cursor is always displayed. Solution: Do not display the cursor when using ":sleep!". (Jeremy Lerner, closes #7688) --- runtime/doc/index.txt | 2 ++ runtime/doc/various.txt | 4 ++-- src/ex_cmds.h | 2 +- src/ex_docmd.c | 13 ++++++++++--- src/normal.c | 4 ++-- src/proto/ex_docmd.pro | 2 +- src/term.c | 2 +- src/testdir/Make_all.mak | 2 ++ src/testdir/test_sleep.vim | 26 ++++++++++++++++++++++++++ src/version.c | 2 ++ 10 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 src/testdir/test_sleep.vim diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 91d22de74b..aec4ca04af 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1590,6 +1590,8 @@ tag command action ~ |:sign| :sig[n] manipulate signs |:silent| :sil[ent] run a command silently |:sleep| :sl[eep] do nothing for a few seconds +|:sleep!| :sl[eep]! do nothing for a few seconds, without the + cursor visible |:slast| :sla[st] split window and go to last file in the argument list |:smagic| :sm[agic] :substitute with 'magic' diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index a895a5eb50..c116d69f2c 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -707,12 +707,12 @@ K Run a program to lookup the keyword under the not more than one line. [N]gs *gs* *:sl* *:sleep* -:[N]sl[eep] [N] [m] Do nothing for [N] seconds. When [m] is included, +:[N]sl[eep] [N][m] Do nothing for [N] seconds. When [m] is included, sleep for [N] milliseconds. The count for "gs" always uses seconds. The default is one second. > :sleep "sleep for one second :5sleep "sleep for five seconds - :sleep 100m "sleep for a hundred milliseconds + :sleep 100m "sleep for 100 milliseconds 10gs "sleep for ten seconds < Can be interrupted with CTRL-C (CTRL-Break on MS-Windows). "gs" stands for "goto sleep". diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 75bb7fb943..30f5221400 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -1365,7 +1365,7 @@ EXCMD(CMD_silent, "silent", ex_wrongmodifier, EX_NEEDARG|EX_EXTRA|EX_BANG|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_sleep, "sleep", ex_sleep, - EX_RANGE|EX_COUNT|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + EX_BANG|EX_RANGE|EX_COUNT|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_OTHER), EXCMD(CMD_slast, "slast", ex_last, EX_EXTRA|EX_BANG|EX_CMDARG|EX_ARGOPT|EX_TRLBAR, diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 7fe33d8e5f..9562e6be3a 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -7225,14 +7225,17 @@ ex_sleep(exarg_T *eap) case NUL: len *= 1000L; break; default: semsg(_(e_invarg2), eap->arg); return; } - do_sleep(len); + + // Hide the cursor if invoked with ! + do_sleep(len, eap->forceit); } /* * Sleep for "msec" milliseconds, but keep checking for a CTRL-C every second. + * Hide the cursor if "hide_cursor" is TRUE. */ void -do_sleep(long msec) +do_sleep(long msec, int hide_cursor) { long done = 0; long wait_now; @@ -7244,7 +7247,11 @@ do_sleep(long msec) ELAPSED_INIT(start_tv); # endif - cursor_on(); + if (hide_cursor) + cursor_off(); + else + cursor_on(); + out_flush_cursor(FALSE, FALSE); while (!got_int && done < msec) { diff --git a/src/normal.c b/src/normal.c index faf6ffbb47..a5f5794c52 100644 --- a/src/normal.c +++ b/src/normal.c @@ -993,7 +993,7 @@ getcount: // something different from CTRL-N. Can't be avoided. while ((c = vpeekc()) <= 0 && towait > 0L) { - do_sleep(towait > 50L ? 50L : towait); + do_sleep(towait > 50L ? 50L : towait, FALSE); towait -= 50L; } if (c > 0) @@ -6230,7 +6230,7 @@ nv_g_cmd(cmdarg_T *cap) * "gs": Goto sleep. */ case 's': - do_sleep(cap->count1 * 1000L); + do_sleep(cap->count1 * 1000L, FALSE); break; /* diff --git a/src/proto/ex_docmd.pro b/src/proto/ex_docmd.pro index 46030a5fd5..94770f2e3c 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -42,7 +42,7 @@ void free_cd_dir(void); void post_chdir(cdscope_T scope); int changedir_func(char_u *new_dir, int forceit, cdscope_T scope); void ex_cd(exarg_T *eap); -void do_sleep(long msec); +void do_sleep(long msec, int hide_cursor); void ex_may_print(exarg_T *eap); void ex_redraw(exarg_T *eap); int vim_mkdir_emsg(char_u *name, int prot); diff --git a/src/term.c b/src/term.c index 5d1228dd7d..47e5dfd1eb 100644 --- a/src/term.c +++ b/src/term.c @@ -2713,7 +2713,7 @@ out_str_cf(char_u *s) else { ++p; - do_sleep(duration); + do_sleep(duration, FALSE); } # else // Rely on the terminal library to sleep. diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 364221fd92..b769499328 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -246,6 +246,7 @@ NEW_TESTS = \ test_shortpathname \ test_signals \ test_signs \ + test_sleep \ test_smartindent \ test_sort \ test_sound \ @@ -472,6 +473,7 @@ NEW_TESTS_RES = \ test_shortpathname.res \ test_signals.res \ test_signs.res \ + test_sleep.res \ test_smartindent.res \ test_sort.res \ test_sound.res \ diff --git a/src/testdir/test_sleep.vim b/src/testdir/test_sleep.vim new file mode 100644 index 0000000000..f71855fd4b --- /dev/null +++ b/src/testdir/test_sleep.vim @@ -0,0 +1,26 @@ +" Test for sleep and sleep! commands + +func! s:get_time_ms() + let timestr = reltimestr(reltime()) + let dotidx = stridx(timestr, '.') + let sec = str2nr(timestr[:dotidx]) + let msec = str2nr(timestr[dotidx + 1:]) + return (sec * 1000) + (msec / 1000) +endfunc + +func! s:assert_takes_longer(cmd, time_ms) + let start = s:get_time_ms() + execute a:cmd + let end = s:get_time_ms() + call assert_true(end - start >=# a:time_ms) +endfun + +func! Test_sleep_bang() + call s:assert_takes_longer('sleep 50m', 50) + call s:assert_takes_longer('sleep! 50m', 50) + call s:assert_takes_longer('sl 50m', 50) + call s:assert_takes_longer('sl! 50m', 50) + call s:assert_takes_longer('1sleep', 1000) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index d0010406d5..0367430ffe 100644 --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 2366, /**/ 2365, /**/