Compare commits

...

46 Commits

Author SHA1 Message Date
373c65104e patch 8.2.1036: popupwin test fails sometimes
Problem:    Popupwin test fails sometimes.
Solution:   Use WaitForAssert() instead of a sleep.
2020-06-22 19:24:23 +02:00
7633fe595e patch 8.2.1035: setreg() does not always clear the register
Problem:    setreg() does not always clear the register.
Solution:   Clear the register if the dict argument is empty. (Andy Massimino,
            closes #3370)
2020-06-22 19:10:56 +02:00
38041da1c2 patch 8.2.1034: compiler warning for uninitialized variables
Problem:    Compiler warning for uninitialized variables.
Solution:   Add initializations. (John Marriott)
2020-06-21 22:17:18 +02:00
73fef33014 Update runtime files 2020-06-21 22:12:03 +02:00
8d9437968b patch 8.2.1033: not easy to read the test time in the test output
Problem:    Not easy to read the test time in the test output.
Solution:   Align the times.  Make slow tests bold.
2020-06-21 20:39:37 +02:00
7fe875583b patch 8.2.1032: error message for declaring a variable cannot be translated
Problem:    Error message for declaring a variable cannot be translated.
Solution:   Enclose in _().  Make environment variable a separate message.
2020-06-21 20:38:28 +02:00
f5433fbfe4 patch 8.2.1031: build failure with Perl5.32
Problem:    Build failure with Perl5.32.
Solution:   Define a few more functions. (Felix Yan, closes #6310)
2020-06-21 20:06:54 +02:00
da58134eed patch 8.2.1030: reducing size of a terminal window may cause a crash
Problem:    Reducing size of a terminal window may cause a crash.
Solution:   Make sure the row and column don't become negative. (closes #6273)
2020-06-21 17:57:32 +02:00
23c5527373 patch 8.2.1029: Vim9: cannot chain function calls with -> at line start
Problem:    Vim9: cannot chain function calls with -> at line start.
Solution:   Peek ahead for a following line starting with "->". (closes #6306)
2020-06-21 16:58:13 +02:00
e55b1c098d patch 8.2.1028: Vim9: no error for declaring buffer, window, etc. variable
Problem:    Vim9: no error for declaring buffer, window, etc. variable.
Solution:   Give an error.  Unify the error messages.
2020-06-21 15:52:59 +02:00
820ffa567c patch 8.2.1027: GUI: multi-byte characters do not work in a terminal
Problem:    GUI: multi-byte characters do not work in a terminal.
Solution:   Do not assume a key is one byte. (closes #6304)
2020-06-21 15:09:14 +02:00
a3b7fdc1bb patch 8.2.1026: Vim9: cannot break the line after "->"
Problem:    Vim9: cannot break the line after "->".
Solution:   Check for a continuation line after "->", "[" and ".".  Ignore
            trailing white space.
2020-06-21 14:12:17 +02:00
8c524f76eb patch 8.2.1025: tabpage menu and tabline not sufficiently tested
Problem:    Tabpage menu and tabline not sufficiently tested.
Solution:   Add tests. (Yegappan Lakshmanan, closes #6307)
2020-06-21 13:23:45 +02:00
6797966dfc patch 8.2.1024: Vim9: no error for using "let g:var = val"
Problem:    Vim9: no error for using "let g:var = val".
Solution:   Add an error.
2020-06-20 22:50:47 +02:00
0cb5bcf583 patch 8.2.1023: Vim9: redefining a function uses a new index every time
Problem:    Vim9: redefining a function uses a new index every time.
Solution:   When redefining a function clear the contents and re-use the
            index.
2020-06-20 18:19:09 +02:00
845e0ee594 patch 8.2.1022: various parts of code not covered by tests
Problem:    Various parts of code not covered by tests.
Solution:   Add more tests. (Yegappan Lakshmanan, closes #6300)
2020-06-20 16:05:32 +02:00
a190548e91 patch 8.2.1021: Ruby interface not tested enough
Problem:    Ruby interface not tested enough.
Solution:   Add a couple more tests. (Dominique Pellé, closes #6301)
2020-06-20 16:00:25 +02:00
b326edf5b3 patch 8.2.1020: popupwin test fails in the GUI
Problem:    Popupwin test fails in the GUI.
Solution:   Send GUI byte sequence for <C-S-a>.
2020-06-20 15:03:38 +02:00
ef6746f637 patch 8.2.1019: mapping <M-S-a> does not work in the GUI
Problem:    Mapping <M-S-a> does not work in the GUI.
Solution:   Move the logic to remove the shift modifier to
            may_remove_shift_modifier() and also use it in the GUI.
2020-06-20 14:43:23 +02:00
280b0dc815 patch 8.2.1018: typo in enum value
Problem:    Typo in enum value. (James McCoy)
Solution:   Fix the typo.
2020-06-20 13:29:03 +02:00
1089374130 patch 8.2.1017: Appveyor output doesn't show MinGW console features
Problem:    Appveyor output doesn't show MinGW console features.
Solution:   List the features of the console build.
2020-06-19 22:37:47 +02:00
7e380030c1 patch 8.2.1016: Vim9: test fails when channel feature is missing
Problem:    Vim9: test fails when channel feature is missing.
Solution:   Process an :if command when skipping
2020-06-19 22:35:44 +02:00
20298ce679 patch 8.2.1015: popup filter gets key with modifier prepended
Problem:    Popup filter gets key with modifier prepended when using
            modifyOtherKeys.
Solution:   Remove the shift modifier when it is included in the key, also
            when the Alt or Meta modifier is used.
2020-06-19 21:46:52 +02:00
1e0b7b11db patch 8.2.1014: using "name" for a string result is confusing
Problem:    Using "name" for a string result is confusing.
Solution:   Rename to "end".
2020-06-19 19:30:53 +02:00
ec9b017b87 patch 8.2.1013: channel tests can be a bit flaky
Problem:    Channel tests can be a bit flaky.
Solution:   Set the g:test_is_flaky flag in SetUp().
2020-06-19 19:10:59 +02:00
3b74b6b4bb patch 8.2.1012: Vim9: cannot declare single character script variables
Problem:    Vim9: cannot declare single character script variables.
Solution:   Don't see "b:", "s:", etc. as namespace.  Fix item size of
            sn_var_vals.
2020-06-19 19:01:43 +02:00
c785b9a7f4 patch 8.2.1011: Vim9: some code not tested
Problem:    Vim9: some code not tested.
Solution:   Add a few more test cases.  Reorder checks for clearer error.
            Remove unreachable code.
2020-06-19 18:34:15 +02:00
128d307963 patch 8.2.1010: build failure in libvterm with debug enabled
Problem:    Build failure in libvterm with debug enabled. (John Little)
Solution:   Use "->" instead of ".".
2020-06-19 17:20:41 +02:00
c5b1c20b6b patch 8.2.1009: Vim9: some failures not checked for
Problem:    Vim9: some failures not checked for.
Solution:   Add test cases.  Remove unused code.
2020-06-18 22:43:27 +02:00
0779fab297 patch 8.2.1008: Vim9: no test for disassambling newly added instructions
Problem:    Vim9: no test for disassambling newly added instructions.
Solution:   Add a function and check disassembly.
2020-06-18 22:18:18 +02:00
c8cb883015 patch 8.2.1007: completion doesn't work after ":r ++arg !"
Problem:    Completion doesn't work after ":r ++arg !".
Solution:   Skip over "++arg". (Christian Brabandt, closes #6275,
            closes #6258)
2020-06-18 21:14:30 +02:00
efd8855594 patch 8.2.1006: Vim9: require unnecessary return statement
Problem:    Vim9: require unnecessary return statement.
Solution:   Improve the use of the had_return flag. (closes #6270)
2020-06-18 20:50:10 +02:00
9b68c82b7c patch 8.2.1005: Vim9: using TRUE/FALSE/MAYBE for ctx_skip is confusing
Problem:    Vim9: using TRUE/FALSE/MAYBE for ctx_skip is confusing.
Solution:   Use an enum value.
2020-06-18 19:31:08 +02:00
511feec6f0 patch 8.2.1004: line numbers below filler lines not always updated
Problem:    Line numbers below filler lines not always updated.
Solution:   Don't break out of the win_line() loop too early. (Christian
            Brabandt, closes #6294, closes #6138)
2020-06-18 19:15:27 +02:00
865af6b990 patch 8.2.1003: Vim9: return type of sort() is too generic
Problem:    Vim9: return type of sort() is too generic.
Solution:   Get type from the first argument. (closes #6292)
2020-06-18 18:45:49 +02:00
ceb2e77510 patch 8.2.1002: test may fail when run directly
Problem:    Test may fail when run directly.
Solution:   Check if g:run_nr exists. (Christian Brabandt, closes #6285)
2020-06-18 18:33:59 +02:00
72abcf42d4 patch 8.2.1001: Vim9: crash with nested "if" and assignment
Problem:    Vim9: crash with nested "if" and assignment.
Solution:   Skip more of the assignment.  Do not set ctx_skip when code is
            reachable.
2020-06-18 18:26:24 +02:00
158ea175a9 patch 8.2.1000: get error when leaving Ex mode with :visual
Problem:    Get error when leaving Ex mode with :visual and a CmdLineEnter
            autocommand was used.
Solution:   Reset ex_pressedreturn. (closes #6293)
2020-06-18 17:28:39 +02:00
2f03e5a0a9 patch 8.2.0999: moving to next sentence gets stuck on quote
Problem:    Moving to next sentence gets stuck on quote.
Solution:   When moving to the next sentence doesn't result in moving, advance
            a character and try again. (closes #6291)
2020-06-18 15:33:25 +02:00
3d9207ad2f patch 8.2.0998: not all tag code is tested
Problem:    Not all tag code is tested.
Solution:   Add a few more test cases. (Yegappan Lakshmanan, closes #6284)
2020-06-17 22:58:35 +02:00
856c1110c1 patch 8.2.0997: cannot execute a register containing line continuation
Problem:    Cannot execute a register containing line continuation.
Solution:   Concatenate lines where needed. (Yegappan Lakshmanan,
            closes #6272)
2020-06-17 21:47:23 +02:00
40a019f157 patch 8.2.0996: using "aucmdwin" in win_gettype() is not ideal
Problem:    Using "aucmdwin" in win_gettype() is not ideal.
Solution:   Rename to "autocmd".
2020-06-17 21:41:35 +02:00
e17f8817a1 patch 8.2.0995: insufficient testing for the readdir() sort option
Problem:    Insufficient testing for the readdir() sort option.
Solution:   Add a few more tests. (Christian Brabandt, closes #6278)
2020-06-17 20:30:44 +02:00
9bb3eb3e02 patch 8.2.0994: Vim9: missing function causes compilation error
Problem:    Vim9: missing function causes compilation error.
Solution:   Call test function indirectly.
2020-06-17 20:03:36 +02:00
f7d267ef20 patch 8.2.0993: Vim9 script test fails with normal features
Problem:    Vim9 script test fails with normal features.
Solution:   Use :func instead of :def for now.
2020-06-17 12:04:54 +02:00
101f4810e2 patch 8.2.0992: Vim9: crash when using :import in the Vim command
Problem:    Vim9: crash when using :import in the Vim command.
Solution:   Give an error when using :import outside of a script.
            (closes #6271)
2020-06-16 23:18:51 +02:00
78 changed files with 1923 additions and 441 deletions

View File

@ -8,6 +8,7 @@ cd src
echo "Building MinGW 32bit console version"
set PATH=c:\msys64\mingw32\bin;%PATH%
mingw32-make.exe -f Make_ming.mak GUI=no OPTIMIZE=speed IME=yes ICONV=yes DEBUG=no FEATURES=%FEATURE% || exit 1
.\vim -u NONE -c "redir @a | ver |0put a | wq" ver_ming.txt
:: Save vim.exe before Make clean, moved back below.
copy vim.exe testdir
mingw32-make.exe -f Make_ming.mak clean
@ -20,13 +21,14 @@ if "%FEATURE%" == "HUGE" (
) ELSE (
mingw32-make.exe -f Make_ming.mak OPTIMIZE=speed GUI=yes IME=yes ICONV=yes DEBUG=no FEATURES=%FEATURE% || exit 1
)
.\gvim -u NONE -c "redir @a | ver |0put a | wq" ver_ming.txt
.\gvim -u NONE -c "redir @a | ver |0put a | wq" ver_ming_gui.txt
:: Filter out the progress bar from the build log
sed -e "s/@<<$/@<< | sed -e 's#.*\\\\r.*##'/" Make_mvc.mak > Make_mvc2.mak
echo "Building MSVC 64bit console Version"
nmake -f Make_mvc2.mak CPU=AMD64 OLE=no GUI=no IME=yes ICONV=yes DEBUG=no FEATURES=%FEATURE% || exit 1
:: The executable is not used
nmake -f Make_mvc2.mak clean
:: build MSVC huge version with python and channel support
@ -43,6 +45,8 @@ if "%FEATURE%" == "HUGE" (
move /Y testdir\vim.exe .
echo "version output MinGW"
type ver_ming.txt
echo "version output MinGW GUI"
type ver_ming_gui.txt
echo "version output MVC"
type ver_msvc.txt
cd ..

View File

@ -1,4 +1,4 @@
*eval.txt* For Vim version 8.2. Last change: 2020 Jun 14
*eval.txt* For Vim version 8.2. Last change: 2020 Jun 17
VIM REFERENCE MANUAL by Bram Moolenaar
@ -3667,7 +3667,7 @@ complete_info([{what}])
"function" User defined completion |i_CTRL-X_CTRL-U|
"omni" Omni completion |i_CTRL-X_CTRL-O|
"spell" Spelling suggestions |i_CTRL-X_s|
"eval" |complete()| completion
"eval" |complete()| completion
"unknown" Other internal modes
If the optional {what} list argument is supplied, then only
@ -4536,7 +4536,7 @@ flatten({list} [, {maxdepth}]) *flatten()*
a very large number.
The {list} is changed in place, make a copy first if you do
not want that.
*E900*
*E900*
{maxdepth} means how deep in nested lists changes are made.
{list} is not modified when {maxdepth} is 0.
{maxdepth} must be positive number.
@ -5163,7 +5163,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
highlight highlight groups
history :history suboptions
locale locale names (as output of locale -a)
mapclear buffer argument
mapclear buffer argument
mapping mapping name
menu menus
messages |:messages| suboptions
@ -5189,7 +5189,7 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()*
If {type} is "cmdline", then the |cmdline-completion| result is
returned. For example, to complete the possible values after
a ":call" command: >
echo getcompletion('call ', 'cmdline')
echo getcompletion('call ', 'cmdline')
<
If there are no matches, an empty list is returned. An
invalid value for {type} produces an error.
@ -5770,8 +5770,8 @@ getwininfo([{winid}]) *getwininfo()*
Returns information about windows as a List with Dictionaries.
If {winid} is given Information about the window with that ID
is returned. If the window does not exist the result is an
empty list.
is returned, as a List with one item. If the window does not
exist the result is an empty list.
Without {winid} information about all the windows in all the
tab pages is returned.
@ -8606,14 +8606,14 @@ searchcount([{options}]) *searchcount()*
if result.total > result.maxcount &&
\ result.current > result.maxcount
return printf(' /%s [>%d/>%d]', @/,
\ result.current, result.total)
\ result.current, result.total)
elseif result.total > result.maxcount
return printf(' /%s [%d/>%d]', @/,
\ result.current, result.total)
\ result.current, result.total)
endif
endif
return printf(' /%s [%d/%d]', @/,
\ result.current, result.total)
\ result.current, result.total)
endfunction
let &statusline .= '%{LastSearchCount()}'
@ -9386,7 +9386,9 @@ simplify({filename}) *simplify()*
Unix) are not resolved. If the first path component in
{filename} designates the current directory, this will be
valid for the result as well. A trailing path separator is
not removed either.
not removed either. On Unix "//path" is unchanged, but
"///path" is simplified to "/path" (this follows the Posix
standard).
Example: >
simplify("./dir/.././/file/") == "./file/"
< Note: The combination "dir/.." is only removed if "dir" is
@ -9696,7 +9698,7 @@ state([{what}]) *state()*
m halfway a mapping, :normal command, feedkeys() or
stuffed command
o operator pending or waiting for a command argument,
e.g. after |f|
e.g. after |f|
a Insert mode autocomplete active
x executing an autocommand
w blocked on waiting, e.g. ch_evalexpr(), ch_read() and
@ -10450,9 +10452,9 @@ terminalprops() *terminalprops()*
If the |+termresponse| feature is missing then the result is
an empty dictionary.
If "cursor_style" is 'y' then |t_RS| will be send to request the
If "cursor_style" is 'y' then |t_RS| will be sent to request the
current cursor style.
If "cursor_blink_mode" is 'y' then |t_RC| will be send to
If "cursor_blink_mode" is 'y' then |t_RC| will be sent to
request the cursor blink status.
"cursor_style" and "cursor_blink_mode" are also set if |t_u7|
is not empty, Vim will detect the working of sending |t_RS|
@ -10864,7 +10866,7 @@ win_getid([{win} [, {tab}]]) *win_getid()*
win_gettype([{nr}]) *win_gettype()*
Return the type of the window:
"aucmdwin" autocommand window. Temporary window
"autocmd" autocommand window. Temporary window
used to execute autocommands.
"popup" popup window |popup|
"preview" preview window |preview-window|
@ -10916,7 +10918,7 @@ win_screenpos({nr}) *win_screenpos()*
GetWinid()->win_screenpos()
<
win_splitmove({nr}, {target} [, {options}]) *win_splitmove()*
Move the window {nr} to a new split of the window {target}.
Move the window {nr} to a new split of the window {target}.
This is similar to moving to {target}, creating a new window
using |:split| but having the same contents as window {nr}, and
then closing {nr}.

View File

@ -1,4 +1,4 @@
*mlang.txt* For Vim version 8.2. Last change: 2019 May 05
*mlang.txt* For Vim version 8.2. Last change: 2020 Jun 16
VIM REFERENCE MANUAL by Bram Moolenaar

View File

@ -163,6 +163,11 @@ q Stops recording. (Implementation note: The 'q' that
result of evaluating the expression is executed as an
Ex command.
Mappings are not recognized in these commands.
When the |line-continuation| character (\) is present
at the beginning of a line in a linewise register,
then it is combined with the previous line. This is
useful for yanking and executing parts of a Vim
script.
Future: Will execute the register for each line in the
address range.

View File

@ -3897,6 +3897,7 @@ E105 mbyte.txt /*E105*
E107 eval.txt /*E107*
E108 eval.txt /*E108*
E109 eval.txt /*E109*
E1094 vim9.txt /*E1094*
E11 cmdline.txt /*E11*
E110 eval.txt /*E110*
E111 eval.txt /*E111*
@ -5798,6 +5799,7 @@ coding-style develop.txt /*coding-style*
col() eval.txt /*col()*
coldfusion.vim syntax.txt /*coldfusion.vim*
collapse tips.txt /*collapse*
collate-variable eval.txt /*collate-variable*
color-xterm syntax.txt /*color-xterm*
coloring syntax.txt /*coloring*
colortest.vim syntax.txt /*colortest.vim*
@ -9723,6 +9725,7 @@ v:charconvert_from eval.txt /*v:charconvert_from*
v:charconvert_to eval.txt /*v:charconvert_to*
v:cmdarg eval.txt /*v:cmdarg*
v:cmdbang eval.txt /*v:cmdbang*
v:collate eval.txt /*v:collate*
v:completed_item eval.txt /*v:completed_item*
v:count eval.txt /*v:count*
v:count1 eval.txt /*v:count1*

View File

@ -1,4 +1,4 @@
*testing.txt* For Vim version 8.2. Last change: 2020 Jun 13
*testing.txt* For Vim version 8.2. Last change: 2020 Jun 15
VIM REFERENCE MANUAL by Bram Moolenaar

View File

@ -1,4 +1,4 @@
*todo.txt* For Vim version 8.2. Last change: 2020 Jun 14
*todo.txt* For Vim version 8.2. Last change: 2020 Jun 21
VIM REFERENCE MANUAL by Bram Moolenaar
@ -40,16 +40,16 @@ browser use: https://github.com/vim/vim/issues/1234
Include src/po/vim.pot ?
See if resizing a terminal can be fixed.
Vim9 script:
Making everything work:
- assignment to script var should check type
- Compile: let [var, var] = expr
share code for :let between compiled and uncompiled?
- do not allow "let g:var = value", must drop "let"
- Error for "g:var: string = 'value'"
- Make func()->append('$') work - value is last argument, not first. #6305
- possible memory leak in test_vim9_func through compile_nested_function.
- memory leaks in test_vim9_expr
- memory leaks in test_vim9_script
- Test that a script-local function in Vim9 script cannot be deleted.
- more return types depending on the first argument, like sort().
- Check that when sourcing a Vim9 script, only the global items can be used.
- Make "true" and "false" work in vim9script
- Test that a function defined inside a :def function is local to that
@ -240,10 +240,12 @@ Terminal emulator window:
conversions.
Error numbers available:
E489, E610, E611, E653, E856, E857, E861
E489, E610, E611, E653, E856
Remove SPACE_IN_FILENAME ? It is only used for completion.
Patch to use collaction based sorting. (Christian Brabandt, #6229)
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.
@ -272,6 +274,8 @@ The buffer list and windows are locked, no changes possible
How about removing Atari MiNT support?
src/Make_mint.mak, src/os_mint.h, matches with __MINT__
Patch to make :q work with local arglist. (Christian Brabandt, #6286)
Patch to fix drawing error with DirectX. (James Grant, #5688)
Causes flicker on resizing. Workaround from Ken Takata.
How about only setting the attribute when part of the Vim window is offscreen?
@ -285,6 +289,8 @@ Motif: Build on Ubuntu can't enter any text in dialog text fields.
Running test_gui and test_gui_init with Motif sometimes kills the window
manager. Problem with Motif?
Patch to add :argdedupe. (Nir Lichtman, #6235)
:map output does not clear the reset of the command line.
(#5623, also see #5962)
@ -1402,9 +1408,6 @@ the system encoding (usually utf-8).
MS-Windows: use WS_HIDE instead of SW_SHOWMINNOACTIVE in os_win32.c?
Otherwise task flickers in taskbar.
Should make ":@r" handle line continuation. (Cesar Romani, 2016 Jun 26)
Also for ":@.".
Repeating 'opfunc' in a function only works once. (Tarmean, 2016 Jul 15, #925)
Have a way to get the call stack, in a function and from an exception.

View File

@ -211,7 +211,7 @@ will automatically delete it:
- The flag that the file was modified is not set.
- The process is not running.
You can programatically deal with this situation with the |FileChangedShell|
You can programmatically deal with this situation with the |FileChangedShell|
autocommand event.

View File

@ -112,7 +112,7 @@ Although it's shorter to do: >
Legacy Vim script does have type checking, but this happens at runtime, when
the code is executed. And it's permissive, often a computation gives an
unexpected value instead of reporting an error . Thus you can define a
unexpected value instead of reporting an error. Thus you can define a
function and think it's fine, but see a problem only later when it is called: >
let s:collected = ''
func ExtendAndReturn(add)
@ -142,7 +142,7 @@ you did wrong: >
Vim9 script is strict, it uses the "+" operator only for numbers and floats.
For string concatenation ".." must be used. This avoids mistakes and avoids
the automatic conversion that gave a suprising result above. So you change
the automatic conversion that gave a surprising result above. So you change
the first line of the function to: >
s:collected ..= add
And now it works.

View File

@ -1,4 +1,4 @@
*usr_toc.txt* For Vim version 8.2. Last change: 2020 Jun 11
*usr_toc.txt* For Vim version 8.2. Last change: 2020 Jun 15
VIM USER MANUAL - by Bram Moolenaar
@ -340,6 +340,12 @@ Make Vim work as you like it.
|45.4| Editing files with a different encoding
|45.5| Entering language text
|usr_46.txt| Write plugins using Vim9 script
|46.1| Introduction
|46.2| Variable declarations
|46.3| Functions and types
|46.?| Using a Vim9 script from legacy script
==============================================================================
Making Vim Run ~

View File

@ -1,4 +1,4 @@
*vim9.txt* For Vim version 8.2. Last change: 2020 Jun 14
*vim9.txt* For Vim version 8.2. Last change: 2020 Jun 21
VIM REFERENCE MANUAL by Bram Moolenaar
@ -30,7 +30,7 @@ THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
Vim script has been growing over time, while preserving backwards
compatibility. That means bad choices from the past often can't be changed
and compability with Vi restricts possible solutions. Execution is quite
and compatibility with Vi restricts possible solutions. Execution is quite
slow, each line is parsed every time it is executed.
The main goal of Vim9 script is to drastically improve performance. This is
@ -68,7 +68,7 @@ In Vim script comments normally start with double quote. That can also be the
start of a string, thus in many places it cannot be used. In Vim9 script a
comment can also start with #. In Vi this is a command to list text with
numbers, but you can also use `:number` for that. >
let count = 0 # number of occurences
let count = 0 # number of occurrences
To improve readability there must be a space between the command and the #
that starts a comment. Note that #{ is the start of a dictionary, therefore
@ -269,6 +269,13 @@ possible AFTER the operators. For example: >
PosFunc(arg) :
NegFunc(arg)
A special case is "->" for function call chains, it can appear in the next
line: >
let result = GetBuilder()
->BuilderSetWidth(333)
->BuilderSetHeight(777)
->BuilderBuild()
Note that "enddef" cannot be used at the start of a continuation line, it ends
the current function.
@ -566,7 +573,7 @@ defined (otherwise script-local) items: >
Import ~
*:import* *:imp*
*:import* *:imp* *E1094*
The exported items can be imported individually in another Vim9 script: >
import EXPORTED_CONST from "thatscript.vim"
import MyClass from "myclass.vim"
@ -692,7 +699,7 @@ A, B and C, where A calls B, B calls C, and C calls A again. It's impossible
to reorder the functions to avoid forward references.
An alternative would be to first scan through the file to locate items and
figure out their type, so that forward refeferences are found, and only then
figure out their type, so that forward references are found, and only then
execute the script and compile the functions. This means the script has to be
parsed twice, which is slower, and some conditions at the script level, such
as checking if a feature is supported, are hard to use. An attempt was made

View File

@ -1,7 +1,7 @@
" Vim support file to detect file types
"
" Maintainer: Bram Moolenaar <Bram@vim.org>
" Last Change: 2020 Jun 07
" Last Change: 2020 Jun 15
" Listen very carefully, I will say this only once
if exists("did_load_filetypes")
@ -237,7 +237,7 @@ au BufNewFile,BufRead */etc/blkid.tab,*/etc/blkid.tab.old setf xml
au BufNewFile,BufRead *bsd,*.bsdl setf bsdl
" Bazel (http://bazel.io)
autocmd BufRead,BufNewFile *.bzl,WORKSPACE,BUILD.bazel setf bzl
autocmd BufRead,BufNewFile *.bzl,WORKSPACE,BUILD.bazel setf bzl
if has("fname_case")
" There is another check for BUILD further below.
autocmd BufRead,BufNewFile BUILD setf bzl
@ -625,8 +625,8 @@ au BufNewFile,BufRead *.mo,*.gdmo setf gdmo
au BufNewFile,BufRead *.ged,lltxxxxx.txt setf gedcom
" Git
au BufNewFile,BufRead COMMIT_EDITMSG,MERGE_MSG,TAG_EDITMSG setf gitcommit
au BufNewFile,BufRead *.git/config,.gitconfig,/etc/gitconfig setf gitconfig
au BufNewFile,BufRead COMMIT_EDITMSG,MERGE_MSG,TAG_EDITMSG setf gitcommit
au BufNewFile,BufRead *.git/config,.gitconfig,/etc/gitconfig setf gitconfig
au BufNewFile,BufRead */.config/git/config setf gitconfig
au BufNewFile,BufRead .gitmodules,*.git/modules/*/config setf gitconfig
if !empty($XDG_CONFIG_HOME)
@ -770,11 +770,11 @@ au BufNewFile,BufRead *.inf,*.INF setf inform
au BufNewFile,BufRead */etc/initng/*/*.i,*.ii setf initng
" Innovation Data Processing
au BufRead,BufNewFile upstream.dat\c,upstream.*.dat\c,*.upstream.dat\c setf upstreamdat
au BufRead,BufNewFile fdrupstream.log,upstream.log\c,upstream.*.log\c,*.upstream.log\c,UPSTREAM-*.log\c setf upstreamlog
au BufRead,BufNewFile upstream.dat\c,upstream.*.dat\c,*.upstream.dat\c setf upstreamdat
au BufRead,BufNewFile fdrupstream.log,upstream.log\c,upstream.*.log\c,*.upstream.log\c,UPSTREAM-*.log\c setf upstreamlog
au BufRead,BufNewFile upstreaminstall.log\c,upstreaminstall.*.log\c,*.upstreaminstall.log\c setf upstreaminstalllog
au BufRead,BufNewFile usserver.log\c,usserver.*.log\c,*.usserver.log\c setf usserverlog
au BufRead,BufNewFile usw2kagt.log\c,usw2kagt.*.log\c,*.usw2kagt.log\c setf usw2kagtlog
au BufRead,BufNewFile usserver.log\c,usserver.*.log\c,*.usserver.log\c setf usserverlog
au BufRead,BufNewFile usw2kagt.log\c,usw2kagt.*.log\c,*.usw2kagt.log\c setf usw2kagtlog
" Ipfilter
au BufNewFile,BufRead ipf.conf,ipf6.conf,ipf.rules setf ipfilter
@ -1073,7 +1073,7 @@ au BufNewFile,BufRead Mutt{ng,}rc setf muttrc
au BufRead,BufNewfile *.n1ql,*.nql setf n1ql
" Nano
au BufNewFile,BufRead */etc/nanorc,*.nanorc setf nanorc
au BufNewFile,BufRead */etc/nanorc,*.nanorc setf nanorc
" Nastran input/DMAP
"au BufNewFile,BufRead *.dat setf nastran
@ -1156,7 +1156,7 @@ au BufNewFile,BufRead *.dpr setf pascal
au BufNewFile,BufRead *.pdf setf pdf
" PCMK - HAE - crm configure edit
au BufNewFile,BufRead *.pcmk setf pcmk
au BufNewFile,BufRead *.pcmk setf pcmk
" Perl
if has("fname_case")
@ -1646,10 +1646,10 @@ au BufNewFile,BufRead *.cm setf voscm
" Swift
au BufNewFile,BufRead *.swift setf swift
au BufNewFile,BufRead *.swift.gyb setf swiftgyb
au BufNewFile,BufRead *.swift.gyb setf swiftgyb
" Swift Intermediate Language
au BufNewFile,BufRead *.sil setf sil
au BufNewFile,BufRead *.sil setf sil
" Sysctl
au BufNewFile,BufRead */etc/sysctl.conf,*/etc/sysctl.d/*.conf setf sysctl
@ -2066,7 +2066,7 @@ au BufNewFile,BufRead *fvwm2rc*
au BufNewFile,BufRead */tmp/lltmp* call s:StarSetf('gedcom')
" Git
au BufNewFile,BufRead */.gitconfig.d/*,/etc/gitconfig.d/* call s:StarSetf('gitconfig')
au BufNewFile,BufRead */.gitconfig.d/*,/etc/gitconfig.d/* call s:StarSetf('gitconfig')
" Gitolite
au BufNewFile,BufRead */gitolite-admin/conf/* call s:StarSetf('gitolite')

View File

@ -1,7 +1,7 @@
" Vim filetype plugin
" Language: Vim
" Maintainer: Bram Moolenaar <Bram@vim.org>
" Last Change: 2020 May 17
" Last Change: 2020 Jun 16
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
@ -83,8 +83,7 @@ endif
if exists("loaded_matchit")
let b:match_ignorecase = 0
let b:match_words =
\ '\<fu\%[nction]\>:\<retu\%[rn]\>:\<endf\%[unction]\>,' .
\ '\<def\>:\<retu\%[rn]\>:\<enddef\>,' .
\ '\<\%(fu\%[nction]\|def\)\>:\<retu\%[rn]\>:\<\%(endf\%[unction]\|enddef\)\>,' .
\ '\<\(wh\%[ile]\|for\)\>:\<brea\%[k]\>:\<con\%[tinue]\>:\<end\(w\%[hile]\|fo\%[r]\)\>,' .
\ '\<if\>:\<el\%[seif]\>:\<en\%[dif]\>,' .
\ '{:},' .

View File

@ -2,7 +2,7 @@
" Header: "{{{
" Maintainer: Bram Moolenaar
" Original Author: Andy Wokula <anwoku@yahoo.de>
" Last Change: 2019 Mar 20
" Last Change: 2020 Jun 18
" Version: 1.0
" Description: HTML indent script with cached state for faster indenting on a
" range of lines.
@ -223,7 +223,7 @@ endfunc "}}}
call s:AddITags(s:indent_tags, [
\ 'a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big',
\ 'blockquote', 'body', 'button', 'caption', 'center', 'cite', 'code',
\ 'colgroup', 'del', 'dfn', 'dir', 'div', 'dl', 'em', 'fieldset', 'font',
\ 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset', 'font',
\ 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html',
\ 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'li',
\ 'map', 'menu', 'noframes', 'noscript', 'object', 'ol',

View File

@ -23,4 +23,14 @@ bar">
text
</div>
<dl>
<dd>
dd text
</dd>
<dt>
dt text
</dt>
</dl>
" END_INDENT

View File

@ -23,4 +23,14 @@
text
</div>
<dl>
<dd>
dd text
</dd>
<dt>
dt text
</dt>
</dl>
" END_INDENT

View File

@ -1,6 +1,6 @@
" Vim plugin for showing matching parens
" Maintainer: Bram Moolenaar <Bram@vim.org>
" Last Change: 2019 Oct 28
" Last Change: 2020 Jun 18
" Exit quickly when:
" - this plugin was already loaded (or disabled)
@ -21,6 +21,7 @@ endif
augroup matchparen
" Replace all matchparen autocommands
autocmd! CursorMoved,CursorMovedI,WinEnter * call s:Highlight_Matching_Pair()
autocmd! WinLeave * call s:Remove_Matches()
if exists('##TextChanged')
autocmd! TextChanged,TextChangedI * call s:Highlight_Matching_Pair()
endif
@ -38,10 +39,7 @@ set cpo-=C
" for any matching paren.
func s:Highlight_Matching_Pair()
" Remove any previous match.
if exists('w:paren_hl_on') && w:paren_hl_on
silent! call matchdelete(3)
let w:paren_hl_on = 0
endif
call s:Remove_Matches()
" Avoid that we remove the popup menu.
" Return when there are no colors (looks like the cursor jumps).
@ -195,6 +193,14 @@ func s:Highlight_Matching_Pair()
endif
endfunction
func s:Remove_Matches()
if exists('w:paren_hl_on') && w:paren_hl_on
silent! call matchdelete(3)
let w:paren_hl_on = 0
endif
endfunc
" Define commands that will disable and enable the plugin.
command DoMatchParen call s:DoMatchParen()
command NoMatchParen call s:NoMatchParen()

View File

@ -1146,6 +1146,15 @@ set_one_cmd_context(
arg = skipwhite(arg);
}
// Skip over ++argopt argument
if ((ea.argt & EX_ARGOPT) && *arg != NUL && STRNCMP(arg, "++", 2) == 0)
{
p = arg;
while (*p && !vim_isspace(*p))
MB_PTR_ADV(p);
arg = skipwhite(p);
}
// Check for '|' to separate commands and '"' to start comments.
// Don't do this for ":read !cmd" and ":write !cmd".
if ((ea.argt & EX_TRLBAR) && !usefilter)

View File

@ -1287,13 +1287,13 @@ win_line(
// When still displaying '$' of change command, stop at cursor.
// When only displaying the (relative) line number and that's done,
// stop here.
if ((dollar_vcol >= 0 && wp == curwin
&& lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol
if (((dollar_vcol >= 0 && wp == curwin
&& lnum == wp->w_cursor.lnum && vcol >= (long)wp->w_virtcol)
|| (number_only && draw_state > WL_NR))
#ifdef FEAT_DIFF
&& filler_todo <= 0
#endif
)
|| (number_only && draw_state > WL_NR))
{
screen_line(screen_row, wp->w_wincol, col, -(int)wp->w_width,
screen_line_flags);

View File

@ -253,7 +253,7 @@ eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
return FAIL;
if (partial->pt_func != NULL
&& partial->pt_func->uf_dfunc_idx != UF_NOT_COMPILED)
&& partial->pt_func->uf_def_status != UF_NOT_COMPILED)
{
if (call_def_function(partial->pt_func, argc, argv,
partial, rettv) == FAIL)

View File

@ -339,6 +339,14 @@ ret_job(int argcount UNUSED, type_T **argtypes UNUSED)
return &t_job;
}
static type_T *
ret_first_arg(int argcount, type_T **argtypes)
{
if (argcount > 0)
return argtypes[0];
return &t_void;
}
static type_T *ret_f_function(int argcount, type_T **argtypes);
/*
@ -849,7 +857,7 @@ static funcentry_T global_functions[] =
{"simplify", 1, 1, FEARG_1, ret_string, f_simplify},
{"sin", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_sin)},
{"sinh", 1, 1, FEARG_1, ret_float, FLOAT_FUNC(f_sinh)},
{"sort", 1, 3, FEARG_1, ret_list_any, f_sort},
{"sort", 1, 3, FEARG_1, ret_first_arg, f_sort},
{"sound_clear", 0, 0, 0, ret_void, SOUND_FUNC(f_sound_clear)},
{"sound_playevent", 1, 2, FEARG_1, ret_number, SOUND_FUNC(f_sound_playevent)},
{"sound_playfile", 1, 2, FEARG_1, ret_number, SOUND_FUNC(f_sound_playfile)},
@ -5994,11 +6002,11 @@ static int srand_seed_for_testing_is_used = FALSE;
f_test_srand_seed(typval_T *argvars, typval_T *rettv UNUSED)
{
if (argvars[0].v_type == VAR_UNKNOWN)
srand_seed_for_testing_is_used = FALSE;
srand_seed_for_testing_is_used = FALSE;
else
{
srand_seed_for_testing = (UINT32_T)tv_get_number(&argvars[0]);
srand_seed_for_testing_is_used = TRUE;
srand_seed_for_testing = (UINT32_T)tv_get_number(&argvars[0]);
srand_seed_for_testing_is_used = TRUE;
}
}
@ -6011,7 +6019,7 @@ init_srand(UINT32_T *x)
if (srand_seed_for_testing_is_used)
{
*x = srand_seed_for_testing;
*x = srand_seed_for_testing;
return;
}
#ifndef MSWIN
@ -7260,6 +7268,37 @@ f_setpos(typval_T *argvars, typval_T *rettv)
}
}
/*
* Translate a register type string to the yank type and block length
*/
static int
get_yank_type(char_u **pp, char_u *yank_type, long *block_len)
{
char_u *stropt = *pp;
switch (*stropt)
{
case 'v': case 'c': // character-wise selection
*yank_type = MCHAR;
break;
case 'V': case 'l': // line-wise selection
*yank_type = MLINE;
break;
case 'b': case Ctrl_V: // block-wise selection
*yank_type = MBLOCK;
if (VIM_ISDIGIT(stropt[1]))
{
++stropt;
*block_len = getdigits(&stropt) - 1;
--stropt;
}
break;
default:
return FAIL;
}
*pp = stropt;
return OK;
}
/*
* "setreg()" function
*/
@ -7294,30 +7333,31 @@ f_setreg(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type == VAR_DICT)
{
dict_T *d = argvars[1].vval.v_dict;
dictitem_T *di = dict_find(d, (char_u *)"regcontents", -1);
dictitem_T *di;
if (d == NULL || d->dv_hashtab.ht_used == 0)
{
// Empty dict, clear the register (like setreg(0, []))
char_u *lstval[2] = {NULL, NULL};
write_reg_contents_lst(regname, lstval, 0, FALSE, MAUTO, -1);
return;
}
di = dict_find(d, (char_u *)"regcontents", -1);
if (di != NULL)
regcontents = &di->di_tv;
stropt = dict_get_string(d, (char_u *)"regtype", FALSE);
if (stropt != NULL)
switch (*stropt)
{
int ret = get_yank_type(&stropt, &yank_type, &block_len);
if (ret == FAIL || *++stropt != NUL)
{
case 'v': // character-wise selection
yank_type = MCHAR;
break;
case 'V': // line-wise selection
yank_type = MLINE;
break;
case Ctrl_V: // block-wise selection
yank_type = MBLOCK;
if (VIM_ISDIGIT(stropt[1]))
{
++stropt;
block_len = getdigits(&stropt) - 1;
--stropt;
}
break;
semsg(_(e_invargval), "value");
return;
}
}
if (regname == '"')
{
@ -7336,6 +7376,12 @@ f_setreg(typval_T *argvars, typval_T *rettv)
if (argvars[2].v_type != VAR_UNKNOWN)
{
if (yank_type != MAUTO)
{
semsg(_(e_toomanyarg), "setreg");
return;
}
stropt = tv_get_string_chk(&argvars[2]);
if (stropt == NULL)
return; // type error
@ -7345,21 +7391,8 @@ f_setreg(typval_T *argvars, typval_T *rettv)
case 'a': case 'A': // append
append = TRUE;
break;
case 'v': case 'c': // character-wise selection
yank_type = MCHAR;
break;
case 'V': case 'l': // line-wise selection
yank_type = MLINE;
break;
case 'b': case Ctrl_V: // block-wise selection
yank_type = MBLOCK;
if (VIM_ISDIGIT(stropt[1]))
{
++stropt;
block_len = getdigits(&stropt) - 1;
--stropt;
}
break;
default:
get_yank_type(&stropt, &yank_type, &block_len);
}
}

View File

@ -1204,6 +1204,13 @@ ex_let_one(
emsg(_("E996: Cannot lock an environment variable"));
return NULL;
}
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
&& (flags & LET_NO_COMMAND) == 0)
{
vim9_declare_error(arg);
return NULL;
}
// Find the end of the name.
++arg;
name = arg;
@ -2628,7 +2635,7 @@ find_var_ht(char_u *name, char_u **varname)
if (*name == 'v') // v: variable
return &vimvarht;
if (get_current_funccal() != NULL
&& get_current_funccal()->func->uf_dfunc_idx == UF_NOT_COMPILED)
&& get_current_funccal()->func->uf_def_status == UF_NOT_COMPILED)
{
// a: and l: are only used in functions defined with ":function"
if (*name == 'a') // a: function argument
@ -2866,6 +2873,15 @@ set_var_const(
}
is_script_local = ht == get_script_local_ht();
if (current_sctx.sc_version == SCRIPT_VERSION_VIM9
&& !is_script_local
&& (flags & LET_NO_COMMAND) == 0
&& name[1] == ':')
{
vim9_declare_error(name);
return;
}
di = find_var_in_ht(ht, 0, varname, TRUE);
// Search in parent scope which is possible to reference from lambda
@ -2886,10 +2902,6 @@ set_var_const(
return;
}
if (var_check_ro(di->di_flags, name, FALSE)
|| var_check_lock(di->di_tv.v_lock, name, FALSE))
return;
if (is_script_local
&& current_sctx.sc_version == SCRIPT_VERSION_VIM9)
{
@ -2900,8 +2912,13 @@ set_var_const(
}
// check the type
check_script_var_type(&di->di_tv, tv, name);
if (check_script_var_type(&di->di_tv, tv, name) == FAIL)
return;
}
if (var_check_ro(di->di_flags, name, FALSE)
|| var_check_lock(di->di_tv.v_lock, name, FALSE))
return;
}
else
// can only redefine once

View File

@ -859,7 +859,7 @@ f_win_gettype(typval_T *argvars, typval_T *rettv)
}
}
if (wp == aucmd_win)
rettv->vval.v_string = vim_strsave((char_u *)"aucmdwin");
rettv->vval.v_string = vim_strsave((char_u *)"autocmd");
#if defined(FEAT_QUICKFIX)
else if (wp->w_p_pvw)
rettv->vval.v_string = vim_strsave((char_u *)"preview");

View File

@ -1895,7 +1895,7 @@ do_one_cmd(
p = ea.cmd;
while (ASCII_ISALNUM(*p))
++p;
p = vim_strnsave(ea.cmd, (int)(p - ea.cmd));
p = vim_strnsave(ea.cmd, p - ea.cmd);
ret = apply_autocmds(EVENT_CMDUNDEFINED, p, p, TRUE, NULL);
vim_free(p);
// If the autocommands did something and didn't cause an error, try
@ -6215,6 +6215,7 @@ do_exedit(
|| eap->cmdidx == CMD_view))
{
exmode_active = FALSE;
ex_pressedreturn = FALSE;
if (*eap->arg == NUL)
{
// Special case: ":global/pat/visual\NLvi-commands"

View File

@ -1788,6 +1788,8 @@ EXTERN char e_no_white_before[] INIT(= N_("E1068: No white space allowed before
EXTERN char e_lock_unlock[] INIT(= N_("E940: Cannot lock or unlock variable %s"));
EXTERN char e_const_req_value[] INIT(= N_("E1021: const requires a value"));
EXTERN char e_type_req[] INIT(= N_("E1022: type or initialization required"));
EXTERN char e_declare_var[] INIT(= N_("E1016: Cannot declare a %s variable: %s"));
EXTERN char e_declare_env_var[] INIT(= N_("E1016: Cannot declare an environment variable: %s"));
#endif
#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
EXTERN char e_alloc_color[] INIT(= N_("E254: Cannot allocate color %s"));

View File

@ -1211,15 +1211,19 @@ key_press_event(GtkWidget *widget UNUSED,
if (len == 0) // Unrecognized key
return TRUE;
// Handle modifiers.
modifiers = modifiers_gdk2vim(state);
// For some keys a shift modifier is translated into another key code.
if (len == -3)
key = TO_SPECIAL(string[1], string[2]);
else
key = string[0];
{
string[len] = NUL;
key = mb_ptr2char(string);
}
// Handle modifiers.
modifiers = modifiers_gdk2vim(state);
// Recognize special keys.
key = simplify_key(key, &modifiers);
if (key == CSI)
key = K_CSI;
@ -1235,8 +1239,11 @@ key_press_event(GtkWidget *widget UNUSED,
// <C-H> and <C-h> mean the same thing, always use "H"
if ((modifiers & MOD_MASK_CTRL) && ASCII_ISALPHA(key))
key = TOUPPER_ASC(key);
string[0] = key;
len = 1;
// May remove the shift modifier if it's included in the key.
modifiers = may_remove_shift_modifier(modifiers, key);
len = mb_char2bytes(key, string);
}
if (modifiers != 0)

View File

@ -938,7 +938,10 @@ gui_x11_key_hit_cb(
if (len == -3)
key = TO_SPECIAL(string[1], string[2]);
else
key = string[0];
{
string[len] = NUL;
key = mb_ptr2char(string);
}
key = simplify_key(key, &modifiers);
if (key == CSI)
key = K_CSI;
@ -951,8 +954,7 @@ gui_x11_key_hit_cb(
}
else
{
string[0] = key;
len = 1;
len = mb_char2bytes(key, string);
// Remove the SHIFT modifier for keys where it's already included,
// e.g., '(', '!' and '*'.

View File

@ -658,6 +658,11 @@ S_SvREFCNT_dec(pTHX_ SV *sv)
}
# endif
/* perl-5.32 needs Perl_SvREFCNT_dec */
# if (PERL_REVISION == 5) && (PERL_VERSION >= 32)
# define Perl_SvREFCNT_dec S_SvREFCNT_dec
# endif
/* perl-5.26 also needs S_TOPMARK and S_POPMARK. */
# if (PERL_REVISION == 5) && (PERL_VERSION >= 26)
PERL_STATIC_INLINE I32
@ -682,6 +687,20 @@ S_POPMARK(pTHX)
}
# endif
/* perl-5.32 needs Perl_POPMARK */
# if (PERL_REVISION == 5) && (PERL_VERSION >= 32)
# define Perl_POPMARK S_POPMARK
/* perl-5.32 needs Perl_SvTRUE */
PERL_STATIC_INLINE bool
Perl_SvTRUE(pTHX_ SV *sv) {
if (!LIKELY(sv))
return FALSE;
SvGETMAGIC(sv);
return SvTRUE_nomg_NN(sv);
}
# endif
/*
* Make all runtime-links of perl.
*

View File

@ -646,6 +646,12 @@ static int setlineinfo(int row, const VTermLineInfo *newinfo, const VTermLineInf
newinfo->doubleheight != oldinfo->doubleheight) {
for(col = 0; col < screen->cols; col++) {
ScreenCell *cell = getcell(screen, row, col);
if (cell == NULL)
{
DEBUG_LOG2("libvterm: setlineinfo() position invalid: %d / %d",
row, col);
return 1;
}
cell->pen.dwl = newinfo->doublewidth;
cell->pen.dhl = newinfo->doubleheight;
}
@ -773,6 +779,12 @@ static size_t _get_chars(const VTermScreen *screen, const int utf8, void *buffer
ScreenCell *cell = getcell(screen, row, col);
int i;
if (cell == NULL)
{
DEBUG_LOG2("libvterm: _get_chars() position invalid: %d / %d",
row, col);
return 1;
}
if(cell->chars[0] == 0)
// Erased cell, might need a space
padding++;

View File

@ -17,11 +17,6 @@ static void putglyph(VTermState *state, const uint32_t chars[], int width, VTerm
{
VTermGlyphInfo info;
if (pos.row >= state->rows)
{
DEBUG_LOG2("libvterm: putglyph() pos.row %d out of range (rows = %d)\n", pos.row, state.rows);
return;
}
info.chars = chars;
info.width = width;
info.protected_cell = state->protected_cell;
@ -289,11 +284,6 @@ static int on_text(const char bytes[], size_t len, void *user)
VTermPos oldpos = state->pos;
if (state->pos.row >= state->rows)
{
DEBUG_LOG2("libvterm: on_text() pos.row %d out of range (rows = %d)\n", state->pos.row, state.rows);
return 0;
}
// We'll have at most len codepoints, plus one from a previous incomplete
// sequence.
codepoints = vterm_allocator_malloc(state->vt, (len + 1) * sizeof(uint32_t));
@ -1856,8 +1846,12 @@ static int on_resize(int rows, int cols, void *user)
if(state->pos.row >= rows)
state->pos.row = rows - 1;
if(state->pos.row < 0)
state->pos.row = 0;
if(state->pos.col >= cols)
state->pos.col = cols - 1;
if(state->pos.col < 0)
state->pos.col = 0;
updatecursor(state, &oldpos, 1);

View File

@ -2910,6 +2910,25 @@ find_special_key(
return 0;
}
/*
* Some keys already have Shift included, pass them as normal keys.
* Not when Ctrl is also used, because <C-H> and <C-S-H> are different.
* Also for <A-S-a> and <M-S-a>.
*/
int
may_remove_shift_modifier(int modifiers, int key)
{
if ((modifiers == MOD_MASK_SHIFT
|| modifiers == (MOD_MASK_SHIFT | MOD_MASK_ALT)
|| modifiers == (MOD_MASK_SHIFT | MOD_MASK_META))
&& ((key >= '@' && key <= 'Z')
|| key == '^' || key == '_'
|| (key >= '{' && key <= '~')))
return modifiers & ~MOD_MASK_SHIFT;
return modifiers;
}
/*
* Try to include modifiers in the key.
* Changes "Shift-a" to 'A', "Alt-A" to 0xc0, etc.
@ -2929,9 +2948,11 @@ extract_modifiers(int key, int *modp, int simplify, int *did_simplify)
if ((modifiers & MOD_MASK_SHIFT) && ASCII_ISALPHA(key))
{
key = TOUPPER_ASC(key);
// With <C-S-a> and <A-S-a> we keep the shift modifier.
// With <S-a> and <S-A> we don't keep the shift modifier.
if (simplify || modifiers == MOD_MASK_SHIFT)
// With <C-S-a> we keep the shift modifier.
// With <S-a>, <A-S-a> and <S-A> we don't keep the shift modifier.
if (simplify || modifiers == MOD_MASK_SHIFT
|| modifiers == (MOD_MASK_SHIFT | MOD_MASK_ALT)
|| modifiers == (MOD_MASK_SHIFT | MOD_MASK_META))
modifiers &= ~MOD_MASK_SHIFT;
}

View File

@ -71,6 +71,7 @@ char_u *get_special_key_name(int c, int modifiers);
int trans_special(char_u **srcp, char_u *dst, int flags, int *did_simplify);
int special_to_buf(int key, int modifiers, int keycode, char_u *dst);
int find_special_key(char_u **srcp, int *modp, int flags, int *did_simplify);
int may_remove_shift_modifier(int modifiers, int key);
int extract_modifiers(int key, int *modp, int simplify, int *did_simplify);
int find_special_key_in_table(int c);
int get_special_key_code(char_u *name);

View File

@ -10,10 +10,11 @@ int get_script_item_idx(int sid, char_u *name, int check_writable);
imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
char_u *to_name_const_end(char_u *arg);
int assignment_len(char_u *p, int *heredoc);
void vim9_declare_error(char_u *name);
int check_vim9_unlet(char_u *name);
int compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx);
void set_function_type(ufunc_T *ufunc);
void delete_instr(isn_T *isn);
void delete_def_function(ufunc_T *ufunc);
void clear_def_function(ufunc_T *ufunc);
void free_def_functions(void);
/* vim: set ft=c : */

View File

@ -7,5 +7,5 @@ void ex_import(exarg_T *eap);
int find_exported(int sid, char_u **argp, int *name_len, ufunc_T **ufunc, type_T **type);
char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx);
char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
void check_script_var_type(typval_T *dest, typval_T *value, char_u *name);
int check_script_var_type(typval_T *dest, typval_T *value, char_u *name);
/* vim: set ft=c : */

View File

@ -473,6 +473,73 @@ set_execreg_lastc(int lastc)
execreg_lastc = lastc;
}
/*
* When executing a register as a series of ex-commands, if the
* line-continuation character is used for a line, then join it with one or
* more previous lines. Note that lines are processed backwards starting from
* the last line in the register.
*
* Arguments:
* lines - list of lines in the register
* idx - index of the line starting with \ or "\. Join this line with all the
* immediate predecessor lines that start with a \ and the first line
* that doesn't start with a \. Lines that start with a comment "\
* character are ignored.
*
* Returns the concatenated line. The index of the line that should be
* processed next is returned in idx.
*/
static char_u *
execreg_line_continuation(char_u **lines, long *idx)
{
garray_T ga;
long i = *idx;
char_u *p;
int cmd_start;
int cmd_end = i;
int j;
char_u *str;
ga_init2(&ga, (int)sizeof(char_u), 400);
// search backwards to find the first line of this command.
// Any line not starting with \ or "\ is the start of the
// command.
while (--i > 0)
{
p = skipwhite(lines[i]);
if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' '))
break;
}
cmd_start = i;
// join all the lines
ga_concat(&ga, lines[cmd_start]);
for (j = cmd_start + 1; j <= cmd_end; j++)
{
p = skipwhite(lines[j]);
if (*p == '\\')
{
// Adjust the growsize to the current length to
// speed up concatenating many lines.
if (ga.ga_len > 400)
{
if (ga.ga_len > 8000)
ga.ga_growsize = 8000;
else
ga.ga_growsize = ga.ga_len;
}
ga_concat(&ga, p + 1);
}
}
ga_append(&ga, NUL);
str = vim_strsave(ga.ga_data);
ga_clear(&ga);
*idx = i;
return str;
}
/*
* Execute a yank register: copy it into the stuff buffer.
*
@ -579,6 +646,8 @@ do_execreg(
for (i = y_current->y_size; --i >= 0; )
{
char_u *escaped;
char_u *str;
int free_str = FALSE;
// insert NL between lines and after last line if type is MLINE
if (y_current->y_type == MLINE || i < y_current->y_size - 1
@ -587,7 +656,23 @@ do_execreg(
if (ins_typebuf((char_u *)"\n", remap, 0, TRUE, silent) == FAIL)
return FAIL;
}
escaped = vim_strsave_escape_csi(y_current->y_array[i]);
// Handle line-continuation for :@<register>
str = y_current->y_array[i];
if (colon && i > 0)
{
p = skipwhite(str);
if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' '))
{
str = execreg_line_continuation(y_current->y_array, &i);
if (str == NULL)
return FAIL;
free_str = TRUE;
}
}
escaped = vim_strsave_escape_csi(str);
if (free_str)
vim_free(str);
if (escaped == NULL)
return FAIL;
retval = ins_typebuf(escaped, remap, 0, TRUE, silent);

View File

@ -1343,7 +1343,7 @@ do_source(
// Allocate the local script variables to use for this script.
new_script_vars(script_items.ga_len);
ga_init2(&si->sn_var_vals, sizeof(typval_T), 10);
ga_init2(&si->sn_var_vals, sizeof(svar_T), 10);
ga_init2(&si->sn_imports, sizeof(imported_T), 10);
ga_init2(&si->sn_type_list, sizeof(type_T), 10);
# ifdef FEAT_PROFILE
@ -1873,7 +1873,7 @@ ex_scriptversion(exarg_T *eap UNUSED)
nr = getdigits(&eap->arg);
if (nr == 0 || *eap->arg != NUL)
emsg(_(e_invarg));
else if (nr > 4)
else if (nr > SCRIPT_VERSION_MAX)
semsg(_("E999: scriptversion not supported: %d"), nr);
else
{

View File

@ -67,6 +67,8 @@ typedef struct terminal_S term_T;
typedef struct VimMenu vimmenu_T;
#endif
// maximum value for sc_version
#define SCRIPT_VERSION_MAX 4
// value for sc_version in a Vim9 script file
#define SCRIPT_VERSION_VIM9 999999
@ -1531,8 +1533,11 @@ struct blobvar_S
typedef struct funccall_S funccall_T;
// values used for "uf_dfunc_idx"
# define UF_NOT_COMPILED -2
# define UF_TO_BE_COMPILED -1
typedef enum {
UF_NOT_COMPILED,
UF_TO_BE_COMPILED,
UF_COMPILED
} def_status_T;
/*
* Structure to hold info for a user function.
@ -1543,7 +1548,8 @@ typedef struct
int uf_flags; // FC_ flags
int uf_calls; // nr of active calls
int uf_cleared; // func_clear() was already called
int uf_dfunc_idx; // UF_NOT_COMPILED, UF_TO_BE_COMPILED or >= 0
def_status_T uf_def_status; // UF_NOT_COMPILED, UF_TO_BE_COMPILED, etc.
int uf_dfunc_idx; // only valid if uf_def_status is UF_COMPILED
garray_T uf_args; // arguments, including optional arguments
garray_T uf_def_args; // default argument expressions

View File

@ -4769,14 +4769,8 @@ handle_key_with_modifier(
modifiers = decode_modifiers(arg[1]);
// Some keys already have Shift included, pass them as
// normal keys. Not when Ctrl is also used, because <C-H>
// and <C-S-H> are different.
if (modifiers == MOD_MASK_SHIFT
&& ((key >= '@' && key <= 'Z')
|| key == '^' || key == '_'
|| (key >= '{' && key <= '~')))
modifiers = 0;
// May remove the shift modifier if it's already included in the key.
modifiers = may_remove_shift_modifier(modifiers, key);
// When used with Ctrl we always make a letter upper case,
// so that mapping <C-H> and <C-h> are the same. Typing

View File

@ -0,0 +1,20 @@
| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0| @33||+1&&|1+0#af5f00255&| @2>a+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|1| |a+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|2| |a+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|x+2#0000000#ff404010| +0&#ffd7ff255@33||+1&#ffffff0| +0#af5f00255&@1|3| |y+2#0000000#ff404010| +0&#ffd7ff255@31
| +0#0000e05#a8a8a8255@1|x+0#0000000#5fd7ff255| @33||+1&#ffffff0| +0#af5f00255&@3|-+0#4040ff13#afffff255@32
| +0#0000e05#a8a8a8255@1|x+0#0000000#5fd7ff255| @33||+1&#ffffff0| +0#af5f00255&@3|-+0#4040ff13#afffff255@32
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|4| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|5| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|6| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|7| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|8| |b+0#0000000&| @31
|~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1
| +0&&@74

View File

@ -0,0 +1,20 @@
| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|1| |a+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0| @33||+1&&|2+0#af5f00255&| @2>a+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|1| |a+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|x+2#0000000#ff404010| +0&#ffd7ff255@33||+1&#ffffff0| +0#af5f00255&@1|2| |y+2#0000000#ff404010| +0&#ffd7ff255@31
| +0#0000e05#a8a8a8255@1|x+0#0000000#5fd7ff255| @33||+1&#ffffff0| +0#af5f00255&@3|-+0#4040ff13#afffff255@32
| +0#0000e05#a8a8a8255@1|x+0#0000000#5fd7ff255| @33||+1&#ffffff0| +0#af5f00255&@3|-+0#4040ff13#afffff255@32
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|3| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|4| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|5| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|6| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|7| |b+0#0000000&| @31
|~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1
| +0&&@74

View File

@ -0,0 +1,20 @@
| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|2| |a+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|1| |a+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|a+0#0000000#ffffff0| @33||+1&&|3+0#af5f00255&| @2>a+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|x+2#0000000#ff404010| +0&#ffd7ff255@33||+1&#ffffff0| +0#af5f00255&@1|1| |y+2#0000000#ff404010| +0&#ffd7ff255@31
| +0#0000e05#a8a8a8255@1|x+0#0000000#5fd7ff255| @33||+1&#ffffff0| +0#af5f00255&@3|-+0#4040ff13#afffff255@32
| +0#0000e05#a8a8a8255@1|x+0#0000000#5fd7ff255| @33||+1&#ffffff0| +0#af5f00255&@3|-+0#4040ff13#afffff255@32
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|2| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|3| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|4| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|5| |b+0#0000000&| @31
| +0#0000e05#a8a8a8255@1|b+0#0000000#ffffff0| @33||+1&&| +0#af5f00255&@1|6| |b+0#0000000&| @31
|~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|~| @35||+1#0000000&|~+0#4040ff13&| @35
|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1
| +0&&@74

View File

@ -216,7 +216,15 @@ func RunTheTest(test)
let message = 'Executed ' . a:test
if has('reltime')
let message ..= ' in ' .. reltimestr(reltime(func_start)) .. ' seconds'
let message ..= repeat(' ', 50 - len(message))
let time = reltime(func_start)
if has('float') && reltimefloat(time) > 0.1
let message = &t_md .. message
endif
let message ..= ' in ' .. reltimestr(time) .. ' seconds'
if has('float') && reltimefloat(time) > 0.1
let message ..= &t_me
endif
endif
call add(s:messages, message)
let s:done += 1
@ -284,7 +292,9 @@ func FinishTesting()
let message = 'Executed ' . s:done . (s:done > 1 ? ' tests' : ' test')
endif
if s:done > 0 && has('reltime')
let message = &t_md .. message .. repeat(' ', 40 - len(message))
let message ..= ' in ' .. reltimestr(reltime(s:start_time)) .. ' seconds'
let message ..= &t_me
endif
echo message
call add(s:messages, message)

View File

@ -28,10 +28,12 @@ endfunc
" The second argument is the minimum time to wait in msec, 10 if omitted.
func TermWait(buf, ...)
let wait_time = a:0 ? a:1 : 10
if g:run_nr == 2
let wait_time *= 4
elseif g:run_nr > 2
let wait_time *= 10
if exists('g:run_nr')
if g:run_nr == 2
let wait_time *= 4
elseif g:run_nr > 2
let wait_time *= 10
endif
endif
call term_wait(a:buf, wait_time)
@ -107,16 +109,18 @@ func RunVimInTerminal(arguments, options)
call TermWait(buf)
" Wait for "All" or "Top" of the ruler to be shown in the last line or in
" the status line of the last window. This can be quite slow (e.g. when
" using valgrind).
" If it fails then show the terminal contents for debugging.
try
call WaitFor({-> len(term_getline(buf, rows)) >= cols - 1 || len(term_getline(buf, rows - statusoff)) >= cols - 1})
catch /timed out after/
let lines = map(range(1, rows), {key, val -> term_getline(buf, val)})
call assert_report('RunVimInTerminal() failed, screen contents: ' . join(lines, "<NL>"))
endtry
if get(a:options, 'wait_for_ruler', 1)
" Wait for "All" or "Top" of the ruler to be shown in the last line or in
" the status line of the last window. This can be quite slow (e.g. when
" using valgrind).
" If it fails then show the terminal contents for debugging.
try
call WaitFor({-> len(term_getline(buf, rows)) >= cols - 1 || len(term_getline(buf, rows - statusoff)) >= cols - 1})
catch /timed out after/
let lines = map(range(1, rows), {key, val -> term_getline(buf, val)})
call assert_report('RunVimInTerminal() failed, screen contents: ' . join(lines, "<NL>"))
endtry
endif
" Starting a terminal to run Vim is always considered flaky.
let g:test_is_flaky = 1

View File

@ -2592,7 +2592,7 @@ func Test_autocmd_window()
augroup END
doautoall BufEnter
call assert_equal([['one.txt', 'aucmdwin'], ['two.txt', '']], g:blist)
call assert_equal([['one.txt', 'autocmd'], ['two.txt', '']], g:blist)
augroup aucmd_win_test
au!

View File

@ -254,6 +254,7 @@ func Test_blob_func_remove()
call assert_fails("call remove(b, 3, 2)", 'E979:')
call assert_fails("call remove(1, 0)", 'E896:')
call assert_fails("call remove(b, b)", 'E974:')
call assert_fails("call remove(b, 1, [])", 'E745:')
call assert_fails("call remove(test_null_blob(), 1, 2)", 'E979:')
endfunc

View File

@ -29,14 +29,14 @@ func SetUp()
endif
let s:chopt = {}
call ch_log(g:testfunc)
" Most tests use job_start(), which can be flaky
let g:test_is_flaky = 1
endfunc
" Run "testfunc" after starting the server and stop the server afterwards.
func s:run_server(testfunc, ...)
call RunServer(s:testscript, a:testfunc, a:000)
" communicating with a server can be flaky
let g:test_is_flaky = 1
endfunc
" Return a list of open files.
@ -455,7 +455,6 @@ endfunc
func Test_connect_waittime()
CheckFunction reltimefloat
" this is timing sensitive
let g:test_is_flaky = 1
let start = reltime()
let handle = ch_open('localhost:9876', s:chopt)
@ -1762,9 +1761,8 @@ func Test_write_to_deleted_buffer()
endfunc
func Test_cmd_parsing()
if !has('unix')
return
endif
CheckUnix
call assert_false(filereadable("file with space"))
let job = job_start('touch "file with space"')
call WaitForAssert({-> assert_true(filereadable("file with space"))})
@ -1963,9 +1961,7 @@ func Test_list_args()
endfunc
func Test_keep_pty_open()
if !has('unix')
return
endif
CheckUnix
let job = job_start(s:python . ' -c "import time;time.sleep(0.2)"',
\ {'out_io': 'null', 'err_io': 'null', 'pty': 1})
@ -2047,9 +2043,7 @@ func Test_no_hang_windows()
endfunc
func Test_job_exitval_and_termsig()
if !has('unix')
return
endif
CheckUnix
" Terminate job normally
let cmd = ['echo']
@ -2123,6 +2117,7 @@ endfunc
" Do this last, it stops any channel log.
func Test_zz_nl_err_to_out_pipe()
eval 'Xlog'->ch_logfile()
call ch_log('Test_zz_nl_err_to_out_pipe()')
let job = job_start(s:python . " test_channel_pipe.py", {'err_io': 'out'})
@ -2214,6 +2209,7 @@ endfunc
func Test_job_trailing_space_unix()
CheckUnix
CheckExecutable cat
let job = job_start("cat ", #{in_io: 'null'})
call WaitForAssert({-> assert_equal("dead", job_status(job))})
call assert_equal(0, job_info(job).exitval)

View File

@ -1568,5 +1568,20 @@ func Test_zero_line_search()
q!
endfunc
func Test_read_shellcmd()
CheckUnix
if executable('ls')
" There should be ls in the $PATH
call feedkeys(":r! l\<c-a>\<c-b>\"\<cr>", 'tx')
call assert_match('^"r! .*\<ls\>', @:)
endif
if executable('rm')
call feedkeys(":r! ++enc=utf-8 r\<c-a>\<c-b>\"\<cr>", 'tx')
call assert_notmatch('^"r!.*\<runtest.vim\>', @:)
call assert_match('^"r!.*\<rm\>', @:)
endif
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -246,6 +246,7 @@ func Test_cpo_H()
endfunc
" TODO: Add a test for the 'i' flag in 'cpo'
" Interrupting the reading of a file will leave it modified.
" Test for the 'I' flag in 'cpo' (deleting autoindent when using arrow keys)
func Test_cpo_I()
@ -294,9 +295,12 @@ func Test_cpo_J()
let &cpo = save_cpo
endfunc
" TODO: Add a test for the 'k' flag in 'cpo'
" TODO: Add a test for the 'k' flag in 'cpo'.
" Disable the recognition of raw key codes in mappings, abbreviations, and the
" "to" part of menu commands.
" TODO: Add a test for the 'K' flag in 'cpo'
" TODO: Add a test for the 'K' flag in 'cpo'.
" Don't wait for a key code to complete when it is halfway a mapping.
" Test for the 'l' flag in 'cpo' (backslash in a [] range)
func Test_cpo_l()
@ -334,7 +338,9 @@ func Test_cpo_L()
let &cpo = save_cpo
endfunc
" TODO: Add a test for the 'm' flag in 'cpo'
" TODO: Add a test for the 'm' flag in 'cpo'.
" When included, a showmatch will always wait half a second. When not
" included, a showmatch will wait half a second or until a character is typed.
" Test for the 'M' flag in 'cpo' (% with escape parenthesis)
func Test_cpo_M()
@ -498,7 +504,9 @@ func Test_cpo_R()
let &cpo = save_cpo
endfunc
" TODO: Add a test for the 's' flag in 'cpo'
" TODO: Add a test for the 's' flag in 'cpo'.
" Set buffer options when entering the buffer for the first time. If not
" present the options are set when the buffer is created.
" Test for the 'S' flag in 'cpo' (copying buffer options)
func Test_cpo_S()
@ -543,8 +551,8 @@ func Test_cpo_u()
let &cpo = save_cpo
endfunc
" TODO: Add a test for the 'v' flag in 'cpo' (backspace doesn't remove
" characters from the screen)
" TODO: Add a test for the 'v' flag in 'cpo'.
" Backspaced characters remain visible on the screen in Insert mode.
" Test for the 'w' flag in 'cpo' ('cw' on a blank character changes only one
" character)

View File

@ -1093,4 +1093,29 @@ func Test_patchexpr()
%bwipe!
endfunc
func Test_diff_rnu()
CheckScreendump
let content =<< trim END
call setline(1, ['a', 'a', 'a', 'y', 'b', 'b', 'b', 'b', 'b'])
vnew
call setline(1, ['a', 'a', 'a', 'x', 'x', 'x', 'b', 'b', 'b', 'b', 'b'])
windo diffthis
setlocal number rnu foldcolumn=0
END
call writefile(content, 'Xtest_diff_rnu')
let buf = RunVimInTerminal('-S Xtest_diff_rnu', {})
call VerifyScreenDump(buf, 'Test_diff_rnu_01', {})
call term_sendkeys(buf, "j")
call VerifyScreenDump(buf, 'Test_diff_rnu_02', {})
call term_sendkeys(buf, "j")
call VerifyScreenDump(buf, 'Test_diff_rnu_03', {})
" clean up
call StopVimInTerminal(buf)
call delete('Xtest_diff_rnu')
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -2,6 +2,7 @@
source check.vim
CheckFeature digraphs
source term_util.vim
func Put_Dig(chars)
exe "norm! o\<c-k>".a:chars
@ -502,4 +503,20 @@ func Test_loadkeymap_error()
call delete('Xkeymap')
endfunc
" Test for the characters displayed on the screen when entering a digraph
func Test_entering_digraph()
CheckRunVimInTerminal
let buf = RunVimInTerminal('', {'rows': 6})
call term_sendkeys(buf, "i\<C-K>")
call term_wait(buf)
call assert_equal('?', term_getline(buf, 1))
call term_sendkeys(buf, "1")
call term_wait(buf)
call assert_equal('1', term_getline(buf, 1))
call term_sendkeys(buf, "2")
call term_wait(buf)
call assert_equal('½', term_getline(buf, 1))
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -401,6 +401,14 @@ func Test_edit_13()
call assert_equal("", getline(2))
call assert_equal(" baz", getline(3))
set autoindent&
" pressing <C-U> to erase line should keep the indent with 'autoindent'
set backspace=2 autoindent
%d
exe "normal i\tone\<CR>three\<C-U>two"
call assert_equal(["\tone", "\ttwo"], getline(1, '$'))
set backspace& autoindent&
bwipe!
endfunc
@ -1301,9 +1309,7 @@ endfunc
func Test_edit_rightleft()
" Cursor in rightleft mode moves differently
if !exists("+rightleft")
return
endif
CheckFeature rightleft
call NewWindow(10, 20)
call setline(1, ['abc', 'def', 'ghi'])
call cursor(1, 2)
@ -1348,6 +1354,13 @@ func Test_edit_rightleft()
\" ihg",
\" ~"]
call assert_equal(join(expect, "\n"), join(lines, "\n"))
%d _
call test_override('redraw_flag', 1)
call test_override('char_avail', 1)
call feedkeys("a\<C-V>x41", "xt")
redraw!
call assert_equal(repeat(' ', 19) .. 'A', Screenline(1))
call test_override('ALL', 0)
set norightleft
bw!
endfunc
@ -1683,4 +1696,103 @@ func Test_edit_file_no_read_perm()
call delete('Xfile')
endfunc
" Pressing escape in 'insertmode' should beep
func Test_edit_insertmode_esc_beeps()
new
set insertmode
call assert_beeps("call feedkeys(\"one\<Esc>\", 'xt')")
set insertmode&
" unsupported CTRL-G command should beep in insert mode.
call assert_beeps("normal i\<C-G>l")
close!
endfunc
" Test for 'hkmap' and 'hkmapp'
func Test_edit_hkmap()
CheckFeature rightleft
if has('win32') && !has('gui')
" Test fails on the MS-Windows terminal version
return
endif
new
set revins hkmap
let str = 'abcdefghijklmnopqrstuvwxyz'
let str ..= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
let str ..= '`/'',.;'
call feedkeys('i' .. str, 'xt')
let expected = "óõú,.;"
let expected ..= "ZYXWVUTSRQPONMLKJIHGFEDCBA"
let expected ..= "æèñ'äåàãø/ôíîöêìçïéòë÷âáðù"
call assert_equal(expected, getline(1))
%d
set revins hkmap hkmapp
let str = 'abcdefghijklmnopqrstuvwxyz'
let str ..= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
call feedkeys('i' .. str, 'xt')
let expected = "õYXWVUTSRQóOïíLKJIHGFEDêBA"
let expected ..= "öòXùåèúæø'ôñðîì÷çéäâóǟãëáà"
call assert_equal(expected, getline(1))
set revins& hkmap& hkmapp&
close!
endfunc
" Test for 'allowrevins' and using CTRL-_ in insert mode
func Test_edit_allowrevins()
CheckFeature rightleft
new
set allowrevins
call feedkeys("iABC\<C-_>DEF\<C-_>GHI", 'xt')
call assert_equal('ABCFEDGHI', getline(1))
set allowrevins&
close!
endfunc
" Test for inserting a register in insert mode using CTRL-R
func Test_edit_insert_reg()
new
let g:Line = ''
func SaveFirstLine()
let g:Line = Screenline(1)
return 'r'
endfunc
inoremap <expr> <buffer> <F2> SaveFirstLine()
call test_override('redraw_flag', 1)
call test_override('char_avail', 1)
let @r = 'sample'
call feedkeys("a\<C-R>=SaveFirstLine()\<CR>", "xt")
call assert_equal('"', g:Line)
call test_override('ALL', 0)
close!
endfunc
" When a character is inserted at the last position of the last line in a
" window, the window contents should be scrolled one line up. If the top line
" is part of a fold, then the entire fold should be scrolled up.
func Test_edit_lastline_scroll()
new
let h = winheight(0)
let lines = ['one', 'two', 'three']
let lines += repeat(['vim'], h - 4)
call setline(1, lines)
call setline(h, repeat('x', winwidth(0) - 1))
call feedkeys("GAx", 'xt')
redraw!
call assert_equal(h - 1, winline())
call assert_equal(2, line('w0'))
" scroll with a fold
1,2fold
normal gg
call setline(h + 1, repeat('x', winwidth(0) - 1))
call feedkeys("GAx", 'xt')
redraw!
call assert_equal(h - 1, winline())
call assert_equal(3, line('w0'))
close!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -166,6 +166,17 @@ func Test_ex_mode_errors()
endtry
call assert_equal(1, caught_e565)
au! InsertCharPre
new
au CmdLineEnter * call ExEnterFunc()
func ExEnterFunc()
endfunc
call feedkeys("gQvi\r", 'xt')
au! CmdLineEnter
delfunc ExEnterFunc
quit
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -2014,6 +2014,25 @@ func Test_readdir_sort()
let files = readdir(dir, '1', #{sort: 'icase'})
call assert_equal(default->sort('i'), files, 'sort by ignoring case')
" 4) collation
let collate = v:collate
lang collate C
let files = readdir(dir, 1, #{sort: 'collate'})
call assert_equal(default->sort(), files, 'sort by C collation')
exe "lang collate" collate
" 5) Errors
call assert_fails('call readdir(dir, 1, 1)', 'E715')
call assert_fails('call readdir(dir, 1, #{sorta: 1})')
call assert_fails('call readdirex(dir, 1, #{sorta: 1})')
" 6) ignore other values in dict
let files = readdir(dir, '1', #{sort: 'c'})
call assert_equal(default, files, 'sort using default2')
" Cleanup
exe "lang collate" collate
eval dir->delete('rf')
endfunc

View File

@ -7,7 +7,7 @@ let s:imstatus_active = 0
func IM_activatefunc(active)
let s:imactivatefunc_called = 1
let s:imstatus_active = a:active
let s:imstatus_active = a:active
endfunc
func IM_statusfunc()
@ -83,4 +83,30 @@ func Test_lmap_in_insert_mode()
close!
endfunc
" Test for using CTRL-^ to toggle iminsert in insert mode
func Test_iminsert_toggle()
CheckGui
if has('win32')
CheckFeature multi_byte_ime
elseif !has('gui_mac')
CheckFeature xim
endif
if has('gui_running') && !has('win32')
" this works only in Win32 GUI version (for some reason)
return
endif
new
let save_imdisable = &imdisable
let save_iminsert = &iminsert
set noimdisable
set iminsert=0
exe "normal i\<C-^>"
call assert_equal(2, &iminsert)
exe "normal i\<C-^>"
call assert_equal(0, &iminsert)
let &iminsert = save_iminsert
let &imdisable = save_imdisable
close!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -79,11 +79,27 @@ func Test_paste_clipboard()
bwipe!
endfunc
" bracketed paste in command line
func Test_paste_cmdline()
call feedkeys(":a\<Esc>[200~foo\<CR>bar\<Esc>[201~b\<Home>\"\<CR>", 'xt')
call assert_equal("\"afoo\<CR>barb", getreg(':'))
endfunc
" bracketed paste in Ex-mode
func Test_paste_ex_mode()
unlet! foo
call feedkeys("Qlet foo=\"\<Esc>[200~foo\<CR>bar\<Esc>[201~\"\<CR>vi\<CR>", 'xt')
call assert_equal("foo\rbar", foo)
endfunc
func Test_paste_onechar()
new
let @f='abc'
call feedkeys("i\<C-R>\<Esc>[200~foo\<CR>bar\<Esc>[201~", 'xt')
call assert_equal("abc", getline(1))
close!
endfunc
func Test_paste_visual_mode()
new
call setline(1, 'here are some words')
@ -134,3 +150,5 @@ func Test_xrestore()
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -2079,9 +2079,9 @@ func Test_popup_scrollbar()
" check size with wrapping lines
call term_sendkeys(buf, "j")
call VerifyScreenDump(buf, 'Test_popupwin_scroll_12', {})
call term_sendkeys(buf, "x")
" clean up
call term_sendkeys(buf, "x")
call StopVimInTerminal(buf)
call delete('XtestPopupScroll')
endfunc
@ -2454,9 +2454,11 @@ func Test_popupwin_terminal_buffer()
call term_sendkeys(termbuf2, "exit\<CR>")
" Exiting shell closes popup window
let pupwin = win_getid()
call feedkeys("exit\<CR>", 'xt')
" Wait for shell to exit
sleep 100m
call WaitForAssert({-> assert_notequal(pupwin, win_getid())})
call feedkeys(":quit\<CR>", 'xt')
call assert_equal(origwin, win_getid())
endfunc
@ -3347,6 +3349,16 @@ func Test_popupwin_filter_input_multibyte()
call feedkeys("\u301b", 'xt')
call assert_equal([0xe3, 0x80, 0x9b], g:bytes)
if has('unix')
" with modifyOtherKeys <M-S-a> does not include a modifier sequence
if has('gui_running')
call feedkeys("\x9b\xfc\x08A", 'Lx!')
else
call feedkeys("\<Esc>[27;4;65~", 'Lx!')
endif
call assert_equal([0xc3, 0x81], g:bytes)
endif
call popup_clear()
delfunc MyPopupFilter
unlet g:bytes

View File

@ -147,6 +147,11 @@ func Test_prompt_buffer_edit()
call assert_beeps('normal! S')
call assert_beeps("normal! \<C-A>")
call assert_beeps("normal! \<C-X>")
" pressing CTRL-W in the prompt buffer should trigger the window commands
call assert_equal(1, winnr())
exe "normal A\<C-W>\<C-W>"
call assert_equal(2, winnr())
wincmd w
close!
call assert_equal(0, prompt_setprompt([], ''))
endfunc

View File

@ -485,6 +485,14 @@ func Test_set_register_dict()
call assert_equal(['six'], getreginfo('0').regcontents)
call assert_equal(['six'], getreginfo('"').regcontents)
let @x = 'one'
call setreg('x', {})
call assert_equal(1, len(split(execute('reg x'), '\n')))
call assert_fails("call setreg('0', #{regtype: 'V'}, 'v')", 'E118:')
call assert_fails("call setreg('0', #{regtype: 'X'})", 'E475:')
call assert_fails("call setreg('0', #{regtype: 'vy'})", 'E475:')
bwipe!
endfunc
@ -557,4 +565,80 @@ func Test_v_register()
bwipe!
endfunc
" Test for executing the contents of a register as an Ex command with line
" continuation.
func Test_execute_reg_as_ex_cmd()
" Line continuation with just two lines
let code =<< trim END
let l = [
\ 1]
END
let @r = code->join("\n")
let l = []
@r
call assert_equal([1], l)
" Line continuation with more than two lines
let code =<< trim END
let l = [
\ 1,
\ 2,
\ 3]
END
let @r = code->join("\n")
let l = []
@r
call assert_equal([1, 2, 3], l)
" use comments interspersed with code
let code =<< trim END
let l = [
"\ one
\ 1,
"\ two
\ 2,
"\ three
\ 3]
END
let @r = code->join("\n")
let l = []
@r
call assert_equal([1, 2, 3], l)
" use line continuation in the middle
let code =<< trim END
let a = "one"
let l = [
\ 1,
\ 2]
let b = "two"
END
let @r = code->join("\n")
let l = []
@r
call assert_equal([1, 2], l)
call assert_equal("one", a)
call assert_equal("two", b)
" only one line with a \
let @r = "\\let l = 1"
call assert_fails('@r', 'E10:')
" only one line with a "\
let @r = ' "\ let i = 1'
@r
call assert_false(exists('i'))
" first line also begins with a \
let @r = "\\let l = [\n\\ 1]"
call assert_fails('@r', 'E10:')
" Test with a large number of lines
let @r = "let str = \n"
let @r ..= repeat(" \\ 'abcdefghijklmnopqrstuvwxyz' ..\n", 312)
let @r ..= ' \ ""'
@r
call assert_equal(repeat('abcdefghijklmnopqrstuvwxyz', 312), str)
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -26,6 +26,18 @@ func Test_rubydo()
%bwipe!
endfunc
func Test_rubydo_dollar_underscore()
new
call setline(1, ['one', 'two', 'three', 'four'])
2,3rubydo $_ = '[' + $_ + ']'
call assert_equal(['one', '[two]', '[three]', 'four'], getline(1, '$'))
bwipe!
call assert_fails('rubydo $_ = 0', 'E265:')
call assert_fails('rubydo (')
bwipe!
endfunc
func Test_rubyfile()
" Check :rubyfile does not SEGV with Ruby level exception but just fails
let tempfile = tempname() . '.rb'
@ -395,6 +407,15 @@ func Test_ruby_p()
call assert_equal(0, len(messages))
endfunc
func Test_rubyeval_error()
" On Linux or Windows the error matches:
" "syntax error, unexpected end-of-input"
" whereas on macOS in CI, the error message makes less sense:
" "SyntaxError: array length must be 2"
" Unclear why. The test does not check the error message.
call assert_fails('call rubyeval("(")')
endfunc
" Test for various heredoc syntax
func Test_ruby_heredoc()
ruby << END

View File

@ -38,6 +38,9 @@ func Test_selectmode_start()
set selectmode=cmd
call feedkeys('gvabc', 'xt')
call assert_equal('abctdef', getline(1))
" arrow keys without shift should not start selection
call feedkeys("A\<Home>\<Right>\<Left>ro", 'xt')
call assert_equal('roabctdef', getline(1))
set selectmode= keymodel=
bw!
endfunc

View File

@ -623,4 +623,93 @@ func Test_tabpage_close_cmdwin()
tabonly
endfunc
" Pressing <C-PageUp> in insert mode should go to the previous tab page
" and <C-PageDown> should go to the next tab page
func Test_tabpage_Ctrl_Pageup()
tabnew
call feedkeys("i\<C-PageUp>", 'xt')
call assert_equal(1, tabpagenr())
call feedkeys("i\<C-PageDown>", 'xt')
call assert_equal(2, tabpagenr())
%bw!
endfunc
" Return the terminal key code for selecting a tab page from the tabline. This
" sequence contains the following codes: a CSI (0x9b), KS_TABLINE (0xf0),
" KS_FILLER (0x58) and then the tab page number.
func TabLineSelectPageCode(tabnr)
return "\x9b\xf0\x58" .. nr2char(a:tabnr)
endfunc
" Return the terminal key code for opening a new tabpage from the tabpage
" menu. This sequence consists of the following codes: a CSI (0x9b),
" KS_TABMENU (0xef), KS_FILLER (0x58), the tab page number and
" TABLINE_MENU_NEW (2).
func TabMenuNewItemCode(tabnr)
return "\x9b\xef\x58" .. nr2char(a:tabnr) .. nr2char(2)
endfunc
" Return the terminal key code for closing a tabpage from the tabpage menu.
" This sequence consists of the following codes: a CSI (0x9b), KS_TABMENU
" (0xef), KS_FILLER (0x58), the tab page number and TABLINE_MENU_CLOSE (1).
func TabMenuCloseItemCode(tabnr)
return "\x9b\xef\x58" .. nr2char(a:tabnr) .. nr2char(1)
endfunc
" Test for using the tabpage menu from the insert and normal modes
func Test_tabline_tabmenu()
" only works in GUI
CheckGui
%bw!
tabnew
tabnew
call assert_equal(3, tabpagenr())
" go to tab page 2 in normal mode
call feedkeys(TabLineSelectPageCode(2), "Lx!")
call assert_equal(2, tabpagenr())
" close tab page 3 in normal mode
call feedkeys(TabMenuCloseItemCode(3), "Lx!")
call assert_equal(2, tabpagenr('$'))
call assert_equal(2, tabpagenr())
" open new tab page before tab page 1 in normal mode
call feedkeys(TabMenuNewItemCode(1), "Lx!")
call assert_equal(1, tabpagenr())
call assert_equal(3, tabpagenr('$'))
" go to tab page 2 in operator-pending mode (should beep)
call assert_beeps('call feedkeys("f" .. TabLineSelectPageCode(2), "Lx!")')
" open new tab page before tab page 1 in operator-pending mode (should beep)
call assert_beeps('call feedkeys("f" .. TabMenuNewItemCode(1), "Lx!")')
" open new tab page after tab page 3 in normal mode
call feedkeys(TabMenuNewItemCode(4), "Lx!")
call assert_equal(4, tabpagenr())
call assert_equal(4, tabpagenr('$'))
" go to tab page 2 in insert mode
call feedkeys("i" .. TabLineSelectPageCode(2) .. "\<C-C>", "Lx!")
call assert_equal(2, tabpagenr())
" close tab page 2 in insert mode
call feedkeys("i" .. TabMenuCloseItemCode(2) .. "\<C-C>", "Lx!")
call assert_equal(3, tabpagenr('$'))
" open new tab page before tab page 3 in insert mode
call feedkeys("i" .. TabMenuNewItemCode(3) .. "\<C-C>", "Lx!")
call assert_equal(3, tabpagenr())
call assert_equal(4, tabpagenr('$'))
" open new tab page after tab page 4 in insert mode
call feedkeys("i" .. TabMenuNewItemCode(5) .. "\<C-C>", "Lx!")
call assert_equal(5, tabpagenr())
call assert_equal(5, tabpagenr('$'))
%bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -240,6 +240,7 @@ func Test_tag_file_encoding()
call delete('Xtags1')
endfunc
" Test for emacs-style tags file (TAGS)
func Test_tagjump_etags()
if !has('emacs_tags')
return
@ -263,8 +264,52 @@ func Test_tagjump_etags()
ta foo
call assert_equal('void foo() {}', getline('.'))
" Test for including another tags file
call writefile([
\ "\x0c",
\ "Xmain.c,64",
\ "void foo() {}\x7ffoo\x011,0",
\ "\x0c",
\ "Xnonexisting,include",
\ "\x0c",
\ "Xtags2,include"
\ ], 'Xtags')
call writefile([
\ "\x0c",
\ "Xmain.c,64",
\ "int main(int argc, char **argv)\x7fmain\x012,14",
\ ], 'Xtags2')
tag main
call assert_equal(2, line('.'))
" corrupted tag line
call writefile([
\ "\x0c",
\ "Xmain.c,8",
\ "int main"
\ ], 'Xtags', 'b')
call assert_fails('tag foo', 'E426:')
" invalid line number
call writefile([
\ "\x0c",
\ "Xmain.c,64",
\ "void foo() {}\x7ffoo\x0abc,0",
\ ], 'Xtags')
call assert_fails('tag foo', 'E426:')
" invalid tag name
call writefile([
\ "\x0c",
\ "Xmain.c,64",
\ ";;;;\x7f1,0",
\ ], 'Xtags')
call assert_fails('tag foo', 'E426:')
call delete('Xtags')
call delete('Xtags2')
call delete('Xmain.c')
set tags&
bwipe!
endfunc
@ -1011,7 +1056,7 @@ func Test_tselect_listing()
call writefile([
\ "!_TAG_FILE_ENCODING\tutf-8\t//",
\ "first\tXfoo\t1" .. ';"' .. "\tv\ttyperef:typename:int\tfile:",
\ "first\tXfoo\t2" .. ';"' .. "\tv\ttyperef:typename:char\tfile:"],
\ "first\tXfoo\t2" .. ';"' .. "\tkind:v\ttyperef:typename:char\tfile:"],
\ 'Xtags')
set tags=Xtags
@ -1268,4 +1313,81 @@ func Test_comment_search()
close!
endfunc
" Test for the 'taglength' option
func Test_tag_length()
set tags=Xtags
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "tame\tXfile1\t1;",
\ "tape\tXfile2\t1;"], 'Xtags')
call writefile(['tame'], 'Xfile1')
call writefile(['tape'], 'Xfile2')
" Jumping to the tag 'tape', should instead jump to 'tame'
new
set taglength=2
tag tape
call assert_equal('Xfile1', @%)
" Tag search should jump to the right tag
enew
tag /^tape$
call assert_equal('Xfile2', @%)
call delete('Xtags')
call delete('Xfile1')
call delete('Xfile2')
set tags& taglength&
endfunc
" Tests for errors in a tags file
func Test_tagfile_errors()
set tags=Xtags
" missing search pattern or line number for a tag
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "foo\tXfile\t"], 'Xtags', 'b')
call writefile(['foo'], 'Xfile')
enew
tag foo
call assert_equal('', @%)
let caught_431 = v:false
try
eval taglist('.*')
catch /:E431:/
let caught_431 = v:true
endtry
call assert_equal(v:true, caught_431)
call delete('Xtags')
call delete('Xfile')
set tags&
endfunc
" When :stag fails to open the file, should close the new window
func Test_stag_close_window_on_error()
new | only
set tags=Xtags
call writefile(["!_TAG_FILE_ENCODING\tutf-8\t//",
\ "foo\tXfile\t1"], 'Xtags')
call writefile(['foo'], 'Xfile')
call writefile([], '.Xfile.swp')
" Remove the catch-all that runtest.vim adds
au! SwapExists
augroup StagTest
au!
autocmd SwapExists Xfile let v:swapchoice='q'
augroup END
stag foo
call assert_equal(1, winnr('$'))
call assert_equal('', @%)
augroup StagTest
au!
augroup END
call delete('Xfile')
call delete('.Xfile.swp')
set tags&
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -1116,6 +1116,20 @@ func Test_fo_a_w()
call feedkeys("iabc abc a abc\<Esc>k0weade", 'xt')
call assert_equal(['abc abcde ', 'a abc'], getline(1, '$'))
" when a line ends with space, it is not broken up.
%d
call feedkeys("ione two to ", 'xt')
call assert_equal('one two to ', getline(1))
" when a line ends with spaces and backspace is used in the next line, the
" last space in the previous line should be removed.
%d
set backspace=indent,eol,start
call setline(1, ['one ', 'two'])
exe "normal 2Gi\<BS>"
call assert_equal(['one two'], getline(1, '$'))
set backspace&
" Test for 'a', 'w' and '1' options.
setlocal textwidth=0
setlocal fo=1aw

View File

@ -311,6 +311,17 @@ func Test_sentence_with_cursor_on_delimiter()
normal! 17|yas
call assert_equal("A '([sentence.])' ", @")
" don't get stuck on a quote at the start of a sentence
%delete _
call setline(1, ['A sentence.', '"A sentence"?', 'A sentence!'])
normal gg))
call assert_equal(3, getcurpos()[1])
%delete _
call setline(1, ['A sentence.', "'A sentence'?", 'A sentence!'])
normal gg))
call assert_equal(3, getcurpos()[1])
%delete _
endfunc

View File

@ -185,6 +185,42 @@ def Test_disassemble_store_member()
res)
enddef
def s:ListAssign()
let x: string
let y: string
let l: list<any>
[x, y; l] = g:stringlist
enddef
def Test_disassemble_list_assign()
let res = execute('disass s:ListAssign')
assert_match('<SNR>\d*_ListAssign\_s*' ..
'let x: string\_s*' ..
'\d PUSHS "\[NULL\]"\_s*' ..
'\d STORE $0\_s*' ..
'let y: string\_s*' ..
'\d PUSHS "\[NULL\]"\_s*' ..
'\d STORE $1\_s*' ..
'let l: list<any>\_s*' ..
'\d NEWLIST size 0\_s*' ..
'\d STORE $2\_s*' ..
'\[x, y; l\] = g:stringlist\_s*' ..
'\d LOADG g:stringlist\_s*' ..
'\d CHECKTYPE list stack\[-1\]\_s*' ..
'\d CHECKLEN >= 2\_s*' ..
'\d\+ ITEM 0\_s*' ..
'\d\+ CHECKTYPE string stack\[-1\]\_s*' ..
'\d\+ STORE $0\_s*' ..
'\d\+ ITEM 1\_s*' ..
'\d\+ CHECKTYPE string stack\[-1\]\_s*' ..
'\d\+ STORE $1\_s*' ..
'\d\+ SLICE 2\_s*' ..
'\d\+ STORE $2\_s*' ..
'\d\+ PUSHNR 0\_s*' ..
'\d\+ RETURN',
res)
enddef
def s:ScriptFuncUnlet()
g:somevar = "value"
unlet g:somevar
@ -533,6 +569,30 @@ def Test_disassemble_const_expr()
assert_notmatch('JUMP', instr)
enddef
def ReturnInIf(): string
if g:cond
return "yes"
else
return "no"
endif
enddef
def Test_disassemble_return_in_if()
let instr = execute('disassemble ReturnInIf')
assert_match('ReturnInIf\_s*' ..
'if g:cond\_s*' ..
'0 LOADG g:cond\_s*' ..
'1 JUMP_IF_FALSE -> 4\_s*' ..
'return "yes"\_s*' ..
'2 PUSHS "yes"\_s*' ..
'3 RETURN\_s*' ..
'else\_s*' ..
' return "no"\_s*' ..
'4 PUSHS "no"\_s*' ..
'5 RETURN$',
instr)
enddef
def WithFunc()
let Funky1: func
let Funky2: func = function("len")
@ -1130,7 +1190,7 @@ def Test_vim9script_forward_func()
def FuncTwo(): string
return 'two'
enddef
let g:res_FuncOne: string = execute('disass FuncOne')
g:res_FuncOne = execute('disass FuncOne')
END
writefile(lines, 'Xdisassemble')
source Xdisassemble

View File

@ -524,6 +524,7 @@ def Test_expr5()
g:anint)
assert_equal(9, g:alsoint + 5)
assert_equal(14, g:alsoint + g:anint)
assert_equal([1, 2, 3, 4], [1] + g:alist)
assert_equal(54, 60 - 6)
assert_equal(50, 60 -
@ -1028,6 +1029,39 @@ def Test_expr7_trailing()
assert_equal(123, d.key)
enddef
def Test_expr7_subscript_linebreak()
let range = range(
3)
let l = range->
map('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range
->map('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range # comment
->map('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range
->map('string(v:key)')
assert_equal(['0', '1', '2'], l)
l = range
# comment
->map('string(v:key)')
assert_equal(['0', '1', '2'], l)
assert_equal('1', l[
1])
let d = #{one: 33}
assert_equal(33, d.
one)
enddef
func Test_expr7_trailing_fails()
call CheckDefFailure(['let l = [2]', 'l->{l -> add(l, 8)}'], 'E107')
@ -1043,7 +1077,7 @@ func Test_expr_fails()
call CheckDefFailure(["CallMe2('yes' , 'no')"], 'E1068:')
call CheckDefFailure(["v:nosuch += 3"], 'E1001:')
call CheckDefFailure(["let v:statusmsg = ''"], 'E1064:')
call CheckDefFailure(["let v:statusmsg = ''"], 'E1016: Cannot declare a v: variable:')
call CheckDefFailure(["let asdf = v:nosuch"], 'E1001:')
call CheckDefFailure(["echo len('asdf'"], 'E110:')

View File

@ -31,6 +31,31 @@ def Test_return_something()
assert_fails('call ReturnGlobal()', 'E1029: Expected number but got string')
enddef
def Test_missing_return()
CheckDefFailure(['def Missing(): number',
' if g:cond',
' echo "no return"',
' else',
' return 0',
' endif'
'enddef'], 'E1027:')
CheckDefFailure(['def Missing(): number',
' if g:cond',
' return 1',
' else',
' echo "no return"',
' endif'
'enddef'], 'E1027:')
CheckDefFailure(['def Missing(): number',
' if g:cond',
' return 1',
' else',
' return 2',
' endif'
' return 3'
'enddef'], 'E1095:')
enddef
let s:nothing = 0
def ReturnNothing()
s:nothing = 1
@ -298,7 +323,7 @@ def Test_vim9script_call()
str->MyFunc()
assert_equal('barfoo', var)
let g:value = 'value'
g:value = 'value'
g:value->MyFunc()
assert_equal('value', var)
@ -807,5 +832,10 @@ def Test_call_closure_not_compiled()
assert_equal('sometext', GetResult(g:Ref))
enddef
def Test_sort_return_type()
let res: list<number>
res = [1, 2, 3]->sort()
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@ -1,6 +1,7 @@
" Test various aspects of the Vim9 script language.
source check.vim
source term_util.vim
source view_util.vim
source vim9.vim
@ -108,6 +109,41 @@ def Test_assignment()
call CheckDefFailure(['v:errmsg += 123'], 'E1013:')
enddef
def Test_vim9_single_char_vars()
let lines =<< trim END
vim9script
" single character variable declarations work
let a: string
let b: number
let l: list<any>
let s: string
let t: number
let v: number
let w: number
" script-local variables can be used without s: prefix
a = 'script-a'
b = 111
l = [1, 2, 3]
s = 'script-s'
t = 222
v = 333
w = 444
assert_equal('script-a', a)
assert_equal(111, b)
assert_equal([1, 2, 3], l)
assert_equal('script-s', s)
assert_equal(222, t)
assert_equal(333, v)
assert_equal(444, w)
END
writefile(lines, 'Xsinglechar')
source Xsinglechar
delete('Xsinglechar')
enddef
def Test_assignment_list()
let list1: list<bool> = [false, true, false]
let list2: list<number> = [1, 2, 3]
@ -126,6 +162,7 @@ def Test_assignment_list()
list2[-3] = 77
assert_equal([77, 88, 99], list2)
call CheckDefExecFailure(['let ll = [1, 2, 3]', 'll[-4] = 6'], 'E684:')
call CheckDefExecFailure(['let [v1, v2] = [1, 2]'], 'E1092:')
# type becomes list<any>
let somelist = rand() > 0 ? [1, 2, 3] : ['a', 'b', 'c']
@ -138,6 +175,9 @@ def Test_assignment_dict()
let dict4: dict<any> = #{one: 1, two: '2'}
let dict5: dict<blob> = #{one: 0z01, two: 0z02}
" overwrite
dict3['key'] = 'another'
call CheckDefExecFailure(['let dd = {}', 'dd[""] = 6'], 'E713:')
# type becomes dict<any>
@ -217,6 +257,13 @@ def Test_assignment_default()
let thechannel: channel
assert_equal(test_null_channel(), thechannel)
if has('unix') && executable('cat')
" check with non-null job and channel, types must match
thejob = job_start("cat ", #{})
thechannel = job_getchannel(thejob)
job_stop(thejob, 'kill')
endif
endif
let nr = 1234 | nr = 5678
@ -275,15 +322,15 @@ def Test_assignment_failure()
call CheckDefFailure(['let &option'], 'E1052:')
call CheckDefFailure(['&g:option = 5'], 'E113:')
call CheckDefFailure(['let $VAR = 5'], 'E1065:')
call CheckDefFailure(['let $VAR = 5'], 'E1016: Cannot declare an environment variable:')
call CheckDefFailure(['let @~ = 5'], 'E354:')
call CheckDefFailure(['let @a = 5'], 'E1066:')
call CheckDefFailure(['let g:var = 5'], 'E1016:')
call CheckDefFailure(['let w:var = 5'], 'E1079:')
call CheckDefFailure(['let b:var = 5'], 'E1078:')
call CheckDefFailure(['let t:var = 5'], 'E1080:')
call CheckDefFailure(['let g:var = 5'], 'E1016: Cannot declare a global variable:')
call CheckDefFailure(['let w:var = 5'], 'E1016: Cannot declare a window variable:')
call CheckDefFailure(['let b:var = 5'], 'E1016: Cannot declare a buffer variable:')
call CheckDefFailure(['let t:var = 5'], 'E1016: Cannot declare a tab variable:')
call CheckDefFailure(['let anr = 4', 'anr ..= "text"'], 'E1019:')
call CheckDefFailure(['let xnr += 4'], 'E1020:')
@ -773,10 +820,37 @@ def Test_vim9script_fails()
CheckScriptFailure(['vim9script', 'export let g:some'], 'E1044:')
CheckScriptFailure(['vim9script', 'export echo 134'], 'E1043:')
CheckScriptFailure(['vim9script', 'let str: string', 'str = 1234'], 'E1013:')
CheckScriptFailure(['vim9script', 'const str = "asdf"', 'str = "xxx"'], 'E46:')
assert_fails('vim9script', 'E1038')
assert_fails('export something', 'E1043')
enddef
func Test_import_fails_without_script()
CheckRunVimInTerminal
" call indirectly to avoid compilation error for missing functions
call Run_Test_import_fails_without_script()
endfunc
def Run_Test_import_fails_without_script()
let export =<< trim END
vim9script
export def Foo(): number
return 0
enddef
END
writefile(export, 'Xexport.vim')
let buf = RunVimInTerminal('-c "import Foo from ''./Xexport.vim''"', #{
rows: 6, wait_for_ruler: 0})
WaitForAssert({-> assert_match('^E1094:', term_getline(buf, 5))})
delete('Xexport.vim')
StopVimInTerminal(buf)
enddef
def Test_vim9script_reload_import()
let lines =<< trim END
vim9script
@ -1025,11 +1099,11 @@ def Test_if_const_expr()
g:glob = 2
if false
execute('let g:glob = 3')
execute('g:glob = 3')
endif
assert_equal(2, g:glob)
if true
execute('let g:glob = 3')
execute('g:glob = 3')
endif
assert_equal(3, g:glob)
@ -1137,6 +1211,26 @@ def Test_if_const_expr_fails()
call CheckDefFailure(["if has('aaa') ? true false"], 'E109:')
enddef
def RunNested(i: number): number
let x: number = 0
if i % 2
if 1
" comment
else
" comment
endif
x += 1
else
x += 1000
endif
return x
enddef
def Test_nested_if()
assert_equal(1, RunNested(1))
assert_equal(1000, RunNested(2))
enddef
def Test_execute_cmd()
new
setline(1, 'default')
@ -1696,8 +1790,8 @@ def Test_vim9_comment_gui()
enddef
def Test_vim9_comment_not_compiled()
au TabEnter *.vim let g:entered = 1
au TabEnter *.x let g:entered = 2
au TabEnter *.vim g:entered = 1
au TabEnter *.x g:entered = 2
edit test.vim
doautocmd TabEnter #comment
@ -1717,14 +1811,46 @@ def Test_vim9_comment_not_compiled()
CheckScriptSuccess([
'vim9script',
'let g:var = 123',
'let w:var = 777',
'g:var = 123',
'b:var = 456',
'w:var = 777',
't:var = 888',
'unlet g:var w:var # something',
])
CheckScriptFailure([
'vim9script',
'let g:var = 123',
], 'E1016: Cannot declare a global variable:')
CheckScriptFailure([
'vim9script',
'let b:var = 123',
], 'E1016: Cannot declare a buffer variable:')
CheckScriptFailure([
'vim9script',
'let w:var = 123',
], 'E1016: Cannot declare a window variable:')
CheckScriptFailure([
'vim9script',
'let t:var = 123',
], 'E1016: Cannot declare a tab variable:')
CheckScriptFailure([
'vim9script',
'let v:version = 123',
], 'E1016: Cannot declare a v: variable:')
CheckScriptFailure([
'vim9script',
'let $VARIABLE = "text"',
], 'E1016: Cannot declare an environment variable:')
CheckScriptFailure([
'vim9script',
'g:var = 123',
'unlet g:var# comment1',
], 'E108:')
@ -1795,11 +1921,11 @@ enddef
def Test_finish()
let lines =<< trim END
vim9script
let g:res = 'one'
g:res = 'one'
if v:false | finish | endif
let g:res = 'two'
g:res = 'two'
finish
let g:res = 'three'
g:res = 'three'
END
writefile(lines, 'Xfinished')
source Xfinished
@ -1875,6 +2001,20 @@ def Test_let_declaration()
unlet g:other_var
enddef
def Test_let_declaration_fails()
let lines =<< trim END
vim9script
const var: string
END
CheckScriptFailure(lines, 'E1021:')
lines =<< trim END
vim9script
let 9var: string
END
CheckScriptFailure(lines, 'E475:')
enddef
def Test_let_type_check()
let lines =<< trim END
vim9script
@ -1888,6 +2028,12 @@ def Test_let_type_check()
let var:string
END
CheckScriptFailure(lines, 'E1069:')
lines =<< trim END
vim9script
let var: asdf
END
CheckScriptFailure(lines, 'E1010:')
enddef
def Test_forward_declaration()

View File

@ -87,6 +87,28 @@ func Test_global_vars()
call assert_equal(test_null, g:MY_GLOBAL_NULL)
call assert_equal(test_none, g:MY_GLOBAL_NONE)
" Test for invalid values for a blob, list, dict in a viminfo file
call writefile([
\ "!GLOB_BLOB_1\tBLO\t123",
\ "!GLOB_BLOB_2\tBLO\t012",
\ "!GLOB_BLOB_3\tBLO\t0z1x",
\ "!GLOB_BLOB_4\tBLO\t0z12 ab",
\ "!GLOB_LIST_1\tLIS\t1 2",
\ "!GLOB_DICT_1\tDIC\t1 2"], 'Xviminfo')
call assert_fails('rv! Xviminfo', 'E15:')
call assert_equal('123', g:GLOB_BLOB_1)
call assert_equal(1, type(g:GLOB_BLOB_1))
call assert_equal('012', g:GLOB_BLOB_2)
call assert_equal(1, type(g:GLOB_BLOB_2))
call assert_equal('0z1x', g:GLOB_BLOB_3)
call assert_equal(1, type(g:GLOB_BLOB_3))
call assert_equal('0z12 ab', g:GLOB_BLOB_4)
call assert_equal(1, type(g:GLOB_BLOB_4))
call assert_equal('1 2', g:GLOB_LIST_1)
call assert_equal(1, type(g:GLOB_LIST_1))
call assert_equal('1 2', g:GLOB_DICT_1)
call assert_equal(1, type(g:GLOB_DICT_1))
call delete('Xviminfo')
set viminfo-=!
endfunc

View File

@ -357,4 +357,22 @@ func Test_delete_break_tab()
close!
endfunc
" Test for using <BS>, <C-W> and <C-U> in virtual edit mode
" to erase character, word and line.
func Test_ve_backspace()
new
call setline(1, 'sample')
set virtualedit=all
set backspace=indent,eol,start
exe "normal 15|i\<BS>\<BS>"
call assert_equal([0, 1, 7, 5], getpos('.'))
exe "normal 15|i\<C-W>"
call assert_equal([0, 1, 6, 0], getpos('.'))
exe "normal 15|i\<C-U>"
call assert_equal([0, 1, 1, 0], getpos('.'))
set backspace&
set virtualedit&
close!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -208,6 +208,15 @@ func Test_virtual_replace()
exe "normal iabcdefghijklmnopqrst\<Esc>0gRAB\tIJKLMNO\tQR"
call assert_equal(['AB......CDEFGHI.Jkl',
\ 'AB IJKLMNO QRst'], getline(12, 13))
" Test inserting Tab with 'noexpandtab' and 'softabstop' set to 4
%d
call setline(1, 'aaaaaaaaaaaaa')
set softtabstop=4
exe "normal gggR\<Tab>\<Tab>x"
call assert_equal("\txaaaa", getline(1))
set softtabstop&
enew!
set noai bs&vim
if exists('save_t_kD')

View File

@ -26,6 +26,7 @@ static int skip_chars(int, int);
findsent(int dir, long count)
{
pos_T pos, tpos;
pos_T prev_pos;
int c;
int (*func)(pos_T *);
int startlnum;
@ -41,6 +42,8 @@ findsent(int dir, long count)
while (count--)
{
prev_pos = pos;
/*
* if on an empty line, skip up to a non-empty line
*/
@ -133,6 +136,18 @@ found:
while (!noskip && ((c = gchar_pos(&pos)) == ' ' || c == '\t'))
if (incl(&pos) == -1)
break;
if (EQUAL_POS(prev_pos, pos))
{
// didn't actually move, advance one character and try again
if ((*func)(&pos) == -1)
{
if (count)
return FAIL;
break;
}
++count;
}
}
setpcmark();

View File

@ -1182,7 +1182,7 @@ get_number_tv(
get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
{
char_u *p;
char_u *name;
char_u *end;
int extra = 0;
int len;
@ -1216,12 +1216,12 @@ get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
// Copy the string into allocated memory, handling backslashed
// characters.
len = (int)(p - *arg + extra);
name = alloc(len);
if (name == NULL)
return FAIL;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = name;
len = (int)(p - *arg + extra);
rettv->vval.v_string = alloc(len);
if (rettv->vval.v_string == NULL)
return FAIL;
end = rettv->vval.v_string;
for (p = *arg + 1; *p != NUL && *p != '"'; )
{
@ -1229,12 +1229,12 @@ get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
{
switch (*++p)
{
case 'b': *name++ = BS; ++p; break;
case 'e': *name++ = ESC; ++p; break;
case 'f': *name++ = FF; ++p; break;
case 'n': *name++ = NL; ++p; break;
case 'r': *name++ = CAR; ++p; break;
case 't': *name++ = TAB; ++p; break;
case 'b': *end++ = BS; ++p; break;
case 'e': *end++ = ESC; ++p; break;
case 'f': *end++ = FF; ++p; break;
case 'n': *end++ = NL; ++p; break;
case 'r': *end++ = CAR; ++p; break;
case 't': *end++ = TAB; ++p; break;
case 'X': // hex: "\x1", "\x12"
case 'x':
@ -1261,9 +1261,9 @@ get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
// For "\u" store the number according to
// 'encoding'.
if (c != 'X')
name += (*mb_char2bytes)(nr, name);
end += (*mb_char2bytes)(nr, end);
else
*name++ = nr;
*end++ = nr;
}
break;
@ -1275,14 +1275,14 @@ get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
case '4':
case '5':
case '6':
case '7': *name = *p++ - '0';
case '7': *end = *p++ - '0';
if (*p >= '0' && *p <= '7')
{
*name = (*name << 3) + *p++ - '0';
*end = (*end << 3) + *p++ - '0';
if (*p >= '0' && *p <= '7')
*name = (*name << 3) + *p++ - '0';
*end = (*end << 3) + *p++ - '0';
}
++name;
++end;
break;
// Special key, e.g.: "\<C-W>"
@ -1292,26 +1292,25 @@ get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
if (p[1] != '*')
flags |= FSK_SIMPLIFY;
extra = trans_special(&p, name, flags, NULL);
extra = trans_special(&p, end, flags, NULL);
if (extra != 0)
{
name += extra;
if (name >= rettv->vval.v_string + len)
end += extra;
if (end >= rettv->vval.v_string + len)
iemsg("get_string_tv() used more space than allocated");
break;
}
}
// FALLTHROUGH
default: MB_COPY_CHAR(p, name);
default: MB_COPY_CHAR(p, end);
break;
}
}
else
MB_COPY_CHAR(p, name);
MB_COPY_CHAR(p, end);
}
*name = NUL;
*end = NUL;
if (*p != NUL) // just in case
++p;
*arg = p;

View File

@ -409,7 +409,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
if (fp == NULL)
goto errret;
fp->uf_dfunc_idx = UF_NOT_COMPILED;
fp->uf_def_status = UF_NOT_COMPILED;
pt = ALLOC_CLEAR_ONE(partial_T);
if (pt == NULL)
goto errret;
@ -1001,7 +1001,7 @@ func_remove(ufunc_T *fp)
{
// When there is a def-function index do not actually remove the
// function, so we can find the index when defining the function again.
if (fp->uf_dfunc_idx >= 0)
if (fp->uf_def_status == UF_COMPILED)
fp->uf_flags |= FC_DEAD;
else
hash_remove(&func_hashtab, hi);
@ -1046,7 +1046,7 @@ func_clear(ufunc_T *fp, int force)
// clear this function
func_clear_items(fp);
funccal_unref(fp->uf_scoped, fp, force);
delete_def_function(fp);
clear_def_function(fp);
}
/*
@ -1074,7 +1074,8 @@ func_free(ufunc_T *fp, int force)
func_clear_free(ufunc_T *fp, int force)
{
func_clear(fp, force);
func_free(fp, force);
if (force || fp->uf_dfunc_idx == 0)
func_free(fp, force);
}
@ -1137,7 +1138,7 @@ call_user_func(
ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1);
func_ptr_ref(fp);
if (fp->uf_dfunc_idx != UF_NOT_COMPILED)
if (fp->uf_def_status != UF_NOT_COMPILED)
{
estack_push_ufunc(fp, 1);
save_current_sctx = current_sctx;
@ -1662,7 +1663,7 @@ free_all_functions(void)
// clear the def function index now
fp = HI2UF(hi);
fp->uf_flags &= ~FC_DEAD;
fp->uf_dfunc_idx = UF_NOT_COMPILED;
fp->uf_def_status = UF_NOT_COMPILED;
// Only free functions that are not refcounted, those are
// supposed to be freed when no longer referenced.
@ -2058,7 +2059,7 @@ list_func_head(ufunc_T *fp, int indent)
msg_start();
if (indent)
msg_puts(" ");
if (fp->uf_dfunc_idx != UF_NOT_COMPILED)
if (fp->uf_def_status != UF_NOT_COMPILED)
msg_puts("def ");
else
msg_puts("function ");
@ -2107,7 +2108,7 @@ list_func_head(ufunc_T *fp, int indent)
}
msg_putchar(')');
if (fp->uf_dfunc_idx != UF_NOT_COMPILED)
if (fp->uf_def_status != UF_NOT_COMPILED)
{
if (fp->uf_ret_type != &t_void)
{
@ -2624,7 +2625,7 @@ def_function(exarg_T *eap, char_u *name_arg)
if (!got_int)
{
msg_putchar('\n');
if (fp->uf_dfunc_idx != UF_NOT_COMPILED)
if (fp->uf_def_status != UF_NOT_COMPILED)
msg_puts(" enddef");
else
msg_puts(" endfunction");
@ -3097,6 +3098,7 @@ def_function(exarg_T *eap, char_u *name_arg)
fp->uf_profiling = FALSE;
fp->uf_prof_initialized = FALSE;
#endif
clear_def_function(fp);
}
}
}
@ -3162,7 +3164,7 @@ def_function(exarg_T *eap, char_u *name_arg)
fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
if (fp == NULL)
goto erret;
fp->uf_dfunc_idx = eap->cmdidx == CMD_def ? UF_TO_BE_COMPILED
fp->uf_def_status = eap->cmdidx == CMD_def ? UF_TO_BE_COMPILED
: UF_NOT_COMPILED;
if (fudi.fd_dict != NULL)
@ -3219,7 +3221,7 @@ def_function(exarg_T *eap, char_u *name_arg)
{
int lnum_save = SOURCING_LNUM;
fp->uf_dfunc_idx = UF_TO_BE_COMPILED;
fp->uf_def_status = UF_TO_BE_COMPILED;
// error messages are for the first function line
SOURCING_LNUM = sourcing_lnum_top;
@ -3289,7 +3291,7 @@ def_function(exarg_T *eap, char_u *name_arg)
SOURCING_LNUM = lnum_save;
}
else
fp->uf_dfunc_idx = UF_NOT_COMPILED;
fp->uf_def_status = UF_NOT_COMPILED;
fp->uf_lines = newlines;
if ((flags & FC_CLOSURE) != 0)
@ -3323,6 +3325,9 @@ def_function(exarg_T *eap, char_u *name_arg)
if (eap->cmdidx == CMD_def)
set_function_type(fp);
else if (fp->uf_script_ctx.sc_version == SCRIPT_VERSION_VIM9)
// :func does not use Vim9 script syntax, even in a Vim9 script file
fp->uf_script_ctx.sc_version = SCRIPT_VERSION_MAX;
goto ret_free;
@ -3372,7 +3377,7 @@ ex_defcompile(exarg_T *eap UNUSED)
--todo;
ufunc = HI2UF(hi);
if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid
&& ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED)
&& ufunc->uf_def_status == UF_TO_BE_COMPILED)
{
compile_def_function(ufunc, FALSE, NULL);

View File

@ -754,6 +754,96 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1036,
/**/
1035,
/**/
1034,
/**/
1033,
/**/
1032,
/**/
1031,
/**/
1030,
/**/
1029,
/**/
1028,
/**/
1027,
/**/
1026,
/**/
1025,
/**/
1024,
/**/
1023,
/**/
1022,
/**/
1021,
/**/
1020,
/**/
1019,
/**/
1018,
/**/
1017,
/**/
1016,
/**/
1015,
/**/
1014,
/**/
1013,
/**/
1012,
/**/
1011,
/**/
1010,
/**/
1009,
/**/
1008,
/**/
1007,
/**/
1006,
/**/
1005,
/**/
1004,
/**/
1003,
/**/
1002,
/**/
1001,
/**/
1000,
/**/
999,
/**/
998,
/**/
997,
/**/
996,
/**/
995,
/**/
994,
/**/
993,
/**/
992,
/**/
991,
/**/

View File

@ -23,6 +23,13 @@
#define DEFINE_VIM9_GLOBALS
#include "vim9.h"
// values for ctx_skip
typedef enum {
SKIP_NOT, // condition is a constant, produce code
SKIP_YES, // condition is a constant, do NOT produce code
SKIP_UNKNOWN // condition is not a constant, produce code
} skip_T;
/*
* Chain of jump instructions where the end label needs to be set.
*/
@ -36,6 +43,8 @@ struct endlabel_S {
* info specific for the scope of :if / elseif / else
*/
typedef struct {
int is_seen_else;
int is_had_return; // every block ends in :return
int is_if_label; // instruction idx at IF or ELSEIF
endlabel_T *is_end_label; // instructions to set end label
} ifscope_T;
@ -83,6 +92,7 @@ struct scope_S {
scope_T *se_outer; // scope containing this one
scopetype_T se_type;
int se_local_count; // ctx_locals.ga_len before scope
skip_T se_skip_save; // ctx_skip before the block
union {
ifscope_T se_if;
whilescope_T se_while;
@ -121,9 +131,9 @@ struct cctx_S {
garray_T ctx_imports; // imported items
int ctx_skip; // when TRUE skip commands, when FALSE skip
// commands after "else"
skip_T ctx_skip;
scope_T *ctx_scope; // current scope, NULL at toplevel
int ctx_had_return; // last seen statement was "return"
cctx_T *ctx_outer; // outer scope for lambda or nested
// function
@ -545,8 +555,8 @@ check_type(type_T *expected, type_T *actual, int give_msg)
/////////////////////////////////////////////////////////////////////
// Following generate_ functions expect the caller to call ga_grow().
#define RETURN_NULL_IF_SKIP(cctx) if (cctx->ctx_skip == TRUE) return NULL
#define RETURN_OK_IF_SKIP(cctx) if (cctx->ctx_skip == TRUE) return OK
#define RETURN_NULL_IF_SKIP(cctx) if (cctx->ctx_skip == SKIP_YES) return NULL
#define RETURN_OK_IF_SKIP(cctx) if (cctx->ctx_skip == SKIP_YES) return OK
/*
* Generate an instruction without arguments.
@ -1483,7 +1493,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
return FAIL;
}
if (ufunc->uf_dfunc_idx != UF_NOT_COMPILED)
if (ufunc->uf_def_status != UF_NOT_COMPILED)
{
int i;
@ -1507,16 +1517,16 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
return FAIL;
}
}
if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED)
if (ufunc->uf_def_status == UF_TO_BE_COMPILED)
if (compile_def_function(ufunc, TRUE, NULL) == FAIL)
return FAIL;
}
if ((isn = generate_instr(cctx,
ufunc->uf_dfunc_idx != UF_NOT_COMPILED ? ISN_DCALL
ufunc->uf_def_status != UF_NOT_COMPILED ? ISN_DCALL
: ISN_UCALL)) == NULL)
return FAIL;
if (ufunc->uf_dfunc_idx != UF_NOT_COMPILED)
if (ufunc->uf_def_status != UF_NOT_COMPILED)
{
isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
isn->isn_arg.dfunc.cdf_argcount = argcount;
@ -2370,12 +2380,43 @@ free_imported(cctx_T *cctx)
ga_clear(&cctx->ctx_imports);
}
/*
* Return TRUE if "p" points at a "#" but not at "#{".
*/
static int
comment_start(char_u *p)
{
return p[0] == '#' && p[1] != '{';
}
/*
* Return a pointer to the next line that isn't empty or only contains a
* comment. Skips over white space.
* Returns NULL if there is none.
*/
static char_u *
peek_next_line(cctx_T *cctx)
{
int lnum = cctx->ctx_lnum;
while (++lnum < cctx->ctx_ufunc->uf_lines.ga_len)
{
char_u *line = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[lnum];
char_u *p = skipwhite(line);
if (*p != NUL && !comment_start(p))
return p;
}
return NULL;
}
/*
* Get the next line of the function from "cctx".
* Skips over empty lines. Skips over comment lines if "skip_comment" is TRUE.
* Returns NULL when at the end.
*/
static char_u *
next_line_from_context(cctx_T *cctx)
next_line_from_context(cctx_T *cctx, int skip_comment)
{
char_u *line;
@ -2390,19 +2431,11 @@ next_line_from_context(cctx_T *cctx)
line = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum];
cctx->ctx_line_start = line;
SOURCING_LNUM = cctx->ctx_lnum + 1;
} while (line == NULL || *skipwhite(line) == NUL);
} while (line == NULL || *skipwhite(line) == NUL
|| (skip_comment && comment_start(skipwhite(line))));
return line;
}
/*
* Return TRUE if "p" points at a "#" but not at "#{".
*/
static int
comment_start(char_u *p)
{
return p[0] == '#' && p[1] != '{';
}
/*
* If "*arg" is at the end of the line, advance to the next line.
* Also when "whitep" points to white space and "*arg" is on a "#".
@ -2413,7 +2446,7 @@ may_get_next_line(char_u *whitep, char_u **arg, cctx_T *cctx)
{
if (**arg == NUL || (VIM_ISWHITE(*whitep) && comment_start(*arg)))
{
char_u *next = next_line_from_context(cctx);
char_u *next = next_line_from_context(cctx, TRUE);
if (next == NULL)
return FAIL;
@ -2493,7 +2526,7 @@ generate_ppconst(cctx_T *cctx, ppconst_T *ppconst)
int ret = OK;
int save_skip = cctx->ctx_skip;
cctx->ctx_skip = FALSE;
cctx->ctx_skip = SKIP_NOT;
for (i = 0; i < ppconst->pp_used; ++i)
if (generate_tv_PUSH(cctx, &ppconst->pp_tv[i]) == FAIL)
ret = FAIL;
@ -2742,14 +2775,8 @@ compile_arguments(char_u **arg, cctx_T *cctx, int *argcount)
for (;;)
{
while (*p == NUL || (VIM_ISWHITE(*whitep) && comment_start(p)))
{
p = next_line_from_context(cctx);
if (p == NULL)
goto failret;
whitep = (char_u *)" ";
p = skipwhite(p);
}
if (may_get_next_line(whitep, &p, cctx) == FAIL)
goto failret;
if (*p == ')')
{
*arg = p + 1;
@ -2976,16 +3003,10 @@ compile_list(char_u **arg, cctx_T *cctx)
for (;;)
{
while (*p == NUL || (VIM_ISWHITE(*whitep) && comment_start(p)))
if (may_get_next_line(whitep, &p, cctx) == FAIL)
{
p = next_line_from_context(cctx);
if (p == NULL)
{
semsg(_(e_list_end), *arg);
return FAIL;
}
whitep = (char_u *)" ";
p = skipwhite(p);
semsg(_(e_list_end), *arg);
return FAIL;
}
if (*p == ']')
{
@ -3032,7 +3053,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
// Compile it into instructions.
compile_def_function(ufunc, TRUE, cctx);
if (ufunc->uf_dfunc_idx >= 0)
if (ufunc->uf_def_status == UF_COMPILED)
return generate_FUNCREF(cctx, ufunc->uf_dfunc_idx);
return FAIL;
}
@ -3102,14 +3123,10 @@ compile_dict(char_u **arg, cctx_T *cctx, int literal)
{
char_u *key = NULL;
while (**arg == NUL || (literal && **arg == '"')
|| (VIM_ISWHITE(*whitep) && comment_start(*arg)))
if (may_get_next_line(whitep, arg, cctx) == FAIL)
{
*arg = next_line_from_context(cctx);
if (*arg == NULL)
goto failret;
whitep = (char_u *)" ";
*arg = skipwhite(*arg);
*arg = NULL;
goto failret;
}
if (**arg == '}')
@ -3169,13 +3186,10 @@ compile_dict(char_u **arg, cctx_T *cctx, int literal)
whitep = *arg + 1;
*arg = skipwhite(*arg + 1);
while (**arg == NUL || (VIM_ISWHITE(*whitep) && comment_start(*arg)))
if (may_get_next_line(whitep, arg, cctx) == FAIL)
{
*arg = next_line_from_context(cctx);
if (*arg == NULL)
goto failret;
whitep = (char_u *)" ";
*arg = skipwhite(*arg);
*arg = NULL;
goto failret;
}
if (compile_expr0(arg, cctx) == FAIL)
@ -3183,15 +3197,11 @@ compile_dict(char_u **arg, cctx_T *cctx, int literal)
++count;
whitep = *arg;
p = skipwhite(*arg);
while (*p == NUL || (VIM_ISWHITE(*whitep) && comment_start(p)))
*arg = skipwhite(*arg);
if (may_get_next_line(whitep, arg, cctx) == FAIL)
{
*arg = next_line_from_context(cctx);
if (*arg == NULL)
goto failret;
whitep = (char_u *)" ";
*arg = skipwhite(*arg);
p = *arg;
*arg = NULL;
goto failret;
}
if (**arg == '}')
break;
@ -3496,6 +3506,24 @@ compile_subscript(
{
for (;;)
{
char_u *p = skipwhite(*arg);
if (*p == NUL || (VIM_ISWHITE(**arg) && comment_start(p)))
{
char_u *next = peek_next_line(cctx);
// If a following line starts with "->{" or "->X" advance to that
// line, so that a line break before "->" is allowed.
if (next != NULL && next[0] == '-' && next[1] == '>'
&& (next[2] == '{' || ASCII_ISALPHA(next[2])))
{
next = next_line_from_context(cctx, TRUE);
if (next == NULL)
return FAIL;
*arg = skipwhite(next);
}
}
if (**arg == '(')
{
garray_T *stack = &cctx->ctx_type_stack;
@ -3516,8 +3544,6 @@ compile_subscript(
}
else if (**arg == '-' && (*arg)[1] == '>')
{
char_u *p;
if (generate_ppconst(cctx, ppconst) == FAIL)
return FAIL;
@ -3528,7 +3554,10 @@ compile_subscript(
return FAIL;
*start_leader = end_leader; // don't apply again later
*arg = skipwhite(*arg + 2);
p = *arg + 2;
*arg = skipwhite(p);
if (may_get_next_line(p, arg, cctx) == FAIL)
return FAIL;
if (**arg == '{')
{
// lambda call: list->{lambda}
@ -3566,7 +3595,10 @@ compile_subscript(
if (generate_ppconst(cctx, ppconst) == FAIL)
return FAIL;
*arg = skipwhite(*arg + 1);
p = *arg + 1;
*arg = skipwhite(p);
if (may_get_next_line(p, arg, cctx) == FAIL)
return FAIL;
if (compile_expr0(arg, cctx) == FAIL)
return FAIL;
@ -3601,14 +3633,14 @@ compile_subscript(
}
else if (**arg == '.' && (*arg)[1] != '.')
{
char_u *p;
if (generate_ppconst(cctx, ppconst) == FAIL)
return FAIL;
++*arg;
p = *arg;
if (may_get_next_line(*arg, arg, cctx) == FAIL)
return FAIL;
// dictionary member: dict.name
p = *arg;
if (eval_isnamec1(*p))
while (eval_isnamec(*p))
MB_PTR_ADV(p);
@ -3851,7 +3883,7 @@ compile_expr7(
}
start_leader = end_leader; // don't apply again below
if (cctx->ctx_skip == TRUE)
if (cctx->ctx_skip == SKIP_YES)
clear_tv(rettv);
else
// A constant expression can possibly be handled compile time,
@ -4325,9 +4357,9 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
garray_T *instr = &cctx->ctx_instr;
garray_T *stack = &cctx->ctx_type_stack;
int alt_idx = instr->ga_len;
int end_idx;
int end_idx = 0;
isn_T *isn;
type_T *type1;
type_T *type1 = NULL;
type_T *type2;
int has_const_expr = FALSE;
int const_value = FALSE;
@ -4347,7 +4379,8 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
const_value = tv2bool(&ppconst->pp_tv[ppconst_used]);
clear_tv(&ppconst->pp_tv[ppconst_used]);
--ppconst->pp_used;
cctx->ctx_skip = save_skip == TRUE || !const_value;
cctx->ctx_skip = save_skip == SKIP_YES || !const_value
? SKIP_YES : SKIP_NOT;
}
else
{
@ -4393,7 +4426,8 @@ compile_expr1(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
// evaluate the third expression
if (has_const_expr)
cctx->ctx_skip = save_skip == TRUE || const_value;
cctx->ctx_skip = save_skip == SKIP_YES || const_value
? SKIP_YES : SKIP_NOT;
*arg = skipwhite(p + 1);
if (may_get_next_line(p + 1, arg, cctx) == FAIL)
return FAIL;
@ -4521,13 +4555,13 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
eap->arg = name_end;
eap->getline = exarg_getline;
eap->cookie = cctx;
eap->skip = cctx->ctx_skip == TRUE;
eap->skip = cctx->ctx_skip == SKIP_YES;
eap->forceit = FALSE;
ufunc = def_function(eap, name);
if (ufunc == NULL)
return NULL;
if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED
if (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, TRUE, cctx) == FAIL)
return NULL;
@ -4638,6 +4672,24 @@ generate_loadvar(
}
}
void
vim9_declare_error(char_u *name)
{
char *scope = "";
switch (*name)
{
case 'g': scope = _("global"); break;
case 'b': scope = _("buffer"); break;
case 'w': scope = _("window"); break;
case 't': scope = _("tab"); break;
case 'v': scope = "v:"; break;
case '$': semsg(_(e_declare_env_var), name); return;
default: return;
}
semsg(_(e_declare_var), scope, name);
}
/*
* Compile declaration and assignment:
* "let var", "let var = expr", "const var = expr" and "var = expr"
@ -4728,7 +4780,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
return NULL;
end = p;
if (cctx->ctx_skip != TRUE)
if (cctx->ctx_skip != SKIP_YES)
{
type_T *stacktype;
@ -4787,7 +4839,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
if (!heredoc)
type = &t_any;
if (cctx->ctx_skip != TRUE)
if (cctx->ctx_skip != SKIP_YES)
{
if (*var_start == '&')
{
@ -4834,8 +4886,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
type = &t_string;
if (is_decl)
{
semsg(_("E1065: Cannot declare an environment variable: %s"),
name);
vim9_declare_error(name);
goto theend;
}
}
@ -4859,8 +4910,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
dest = dest_global;
if (is_decl)
{
semsg(_("E1016: Cannot declare a global variable: %s"),
name);
vim9_declare_error(name);
goto theend;
}
}
@ -4869,8 +4919,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
dest = dest_buffer;
if (is_decl)
{
semsg(_("E1078: Cannot declare a buffer variable: %s"),
name);
vim9_declare_error(name);
goto theend;
}
}
@ -4879,8 +4928,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
dest = dest_window;
if (is_decl)
{
semsg(_("E1079: Cannot declare a window variable: %s"),
name);
vim9_declare_error(name);
goto theend;
}
}
@ -4889,7 +4937,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
dest = dest_tab;
if (is_decl)
{
semsg(_("E1080: Cannot declare a tab variable: %s"), name);
vim9_declare_error(name);
goto theend;
}
}
@ -4912,7 +4960,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
type = typval2type(vtv);
if (is_decl)
{
semsg(_("E1064: Cannot declare a v: variable: %s"), name);
vim9_declare_error(name);
goto theend;
}
}
@ -5013,7 +5061,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
goto theend;
}
if (lvar == NULL && dest == dest_local && cctx->ctx_skip != TRUE)
if (lvar == NULL && dest == dest_local && cctx->ctx_skip != SKIP_YES)
{
if (oplen > 1 && !heredoc)
{
@ -5067,8 +5115,19 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
if (!heredoc)
{
if (oplen > 0)
if (cctx->ctx_skip == SKIP_YES)
{
if (oplen > 0 && var_count == 0)
{
// skip over the "=" and the expression
p = skipwhite(op + oplen);
compile_expr0(&p, cctx);
}
}
else if (oplen > 0)
{
type_T *stacktype;
// For "var = expr" evaluate the expression.
if (var_count == 0)
{
@ -5113,52 +5172,47 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
return FAIL;
}
if (cctx->ctx_skip != TRUE)
stacktype = stack->ga_len == 0 ? &t_void
: ((type_T **)stack->ga_data)[stack->ga_len - 1];
if (lvar != NULL && (is_decl || !has_type))
{
type_T *stacktype;
stacktype = stack->ga_len == 0 ? &t_void
: ((type_T **)stack->ga_data)[stack->ga_len - 1];
if (lvar != NULL && (is_decl || !has_type))
if (new_local && !has_type)
{
if (new_local && !has_type)
if (stacktype->tt_type == VAR_VOID)
{
if (stacktype->tt_type == VAR_VOID)
{
emsg(_(e_cannot_use_void));
goto theend;
}
else
{
// An empty list or dict has a &t_void member,
// for a variable that implies &t_any.
if (stacktype == &t_list_empty)
lvar->lv_type = &t_list_any;
else if (stacktype == &t_dict_empty)
lvar->lv_type = &t_dict_any;
else
lvar->lv_type = stacktype;
}
emsg(_(e_cannot_use_void));
goto theend;
}
else
{
type_T *use_type = lvar->lv_type;
if (has_index)
{
use_type = use_type->tt_member;
if (use_type == NULL)
use_type = &t_void;
}
if (need_type(stacktype, use_type, -1, cctx)
== FAIL)
goto theend;
// An empty list or dict has a &t_void member,
// for a variable that implies &t_any.
if (stacktype == &t_list_empty)
lvar->lv_type = &t_list_any;
else if (stacktype == &t_dict_empty)
lvar->lv_type = &t_dict_any;
else
lvar->lv_type = stacktype;
}
}
else if (*p != '=' && need_type(stacktype, member_type, -1,
cctx) == FAIL)
goto theend;
else
{
type_T *use_type = lvar->lv_type;
if (has_index)
{
use_type = use_type->tt_member;
if (use_type == NULL)
use_type = &t_void;
}
if (need_type(stacktype, use_type, -1, cctx)
== FAIL)
goto theend;
}
}
else if (*p != '=' && need_type(stacktype, member_type, -1,
cctx) == FAIL)
goto theend;
}
else if (cmdidx == CMD_const)
{
@ -5220,6 +5274,10 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
end = p;
}
// no need to parse more when skipping
if (cctx->ctx_skip == SKIP_YES)
break;
if (oplen > 0 && *op != '=')
{
type_T *expected = &t_number;
@ -5646,6 +5704,7 @@ compile_if(char_u *arg, cctx_T *cctx)
garray_T *instr = &cctx->ctx_instr;
int instr_count = instr->ga_len;
scope_T *scope;
skip_T skip_save = cctx->ctx_skip;
ppconst_T ppconst;
CLEAR_FIELD(ppconst);
@ -5654,17 +5713,18 @@ compile_if(char_u *arg, cctx_T *cctx)
clear_ppconst(&ppconst);
return NULL;
}
if (instr->ga_len == instr_count && ppconst.pp_used == 1)
if (cctx->ctx_skip == SKIP_YES)
clear_ppconst(&ppconst);
else if (instr->ga_len == instr_count && ppconst.pp_used == 1)
{
// The expression results in a constant.
// TODO: how about nesting?
cctx->ctx_skip = tv2bool(&ppconst.pp_tv[0]) ? FALSE : TRUE;
cctx->ctx_skip = tv2bool(&ppconst.pp_tv[0]) ? SKIP_NOT : SKIP_YES;
clear_ppconst(&ppconst);
}
else
{
// Not a constant, generate instructions for the expression.
cctx->ctx_skip = MAYBE;
cctx->ctx_skip = SKIP_UNKNOWN;
if (generate_ppconst(cctx, &ppconst) == FAIL)
return NULL;
}
@ -5672,8 +5732,11 @@ compile_if(char_u *arg, cctx_T *cctx)
scope = new_scope(cctx, IF_SCOPE);
if (scope == NULL)
return NULL;
scope->se_skip_save = skip_save;
// "is_had_return" will be reset if any block does not end in :return
scope->se_u.se_if.is_had_return = TRUE;
if (cctx->ctx_skip == MAYBE)
if (cctx->ctx_skip == SKIP_UNKNOWN)
{
// "where" is set when ":elseif", "else" or ":endif" is found
scope->se_u.se_if.is_if_label = instr->ga_len;
@ -5701,8 +5764,10 @@ compile_elseif(char_u *arg, cctx_T *cctx)
return NULL;
}
unwind_locals(cctx, scope->se_local_count);
if (!cctx->ctx_had_return)
scope->se_u.se_if.is_had_return = FALSE;
if (cctx->ctx_skip == MAYBE)
if (cctx->ctx_skip == SKIP_UNKNOWN)
{
if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
JUMP_ALWAYS, cctx) == FAIL)
@ -5719,18 +5784,20 @@ compile_elseif(char_u *arg, cctx_T *cctx)
clear_ppconst(&ppconst);
return NULL;
}
if (instr->ga_len == instr_count && ppconst.pp_used == 1)
if (scope->se_skip_save == SKIP_YES)
clear_ppconst(&ppconst);
else if (instr->ga_len == instr_count && ppconst.pp_used == 1)
{
// The expression results in a constant.
// TODO: how about nesting?
cctx->ctx_skip = tv2bool(&ppconst.pp_tv[0]) ? FALSE : TRUE;
cctx->ctx_skip = tv2bool(&ppconst.pp_tv[0]) ? SKIP_NOT : SKIP_YES;
clear_ppconst(&ppconst);
scope->se_u.se_if.is_if_label = -1;
}
else
{
// Not a constant, generate instructions for the expression.
cctx->ctx_skip = MAYBE;
cctx->ctx_skip = SKIP_UNKNOWN;
if (generate_ppconst(cctx, &ppconst) == FAIL)
return NULL;
@ -5756,28 +5823,35 @@ compile_else(char_u *arg, cctx_T *cctx)
return NULL;
}
unwind_locals(cctx, scope->se_local_count);
if (!cctx->ctx_had_return)
scope->se_u.se_if.is_had_return = FALSE;
scope->se_u.se_if.is_seen_else = TRUE;
// jump from previous block to the end, unless the else block is empty
if (cctx->ctx_skip == MAYBE)
if (scope->se_skip_save != SKIP_YES)
{
if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
JUMP_ALWAYS, cctx) == FAIL)
return NULL;
}
if (cctx->ctx_skip == MAYBE)
{
if (scope->se_u.se_if.is_if_label >= 0)
// jump from previous block to the end, unless the else block is empty
if (cctx->ctx_skip == SKIP_UNKNOWN)
{
// previous "if" or "elseif" jumps here
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
isn->isn_arg.jump.jump_where = instr->ga_len;
scope->se_u.se_if.is_if_label = -1;
if (!cctx->ctx_had_return
&& compile_jump_to_end(&scope->se_u.se_if.is_end_label,
JUMP_ALWAYS, cctx) == FAIL)
return NULL;
}
}
if (cctx->ctx_skip != MAYBE)
cctx->ctx_skip = !cctx->ctx_skip;
if (cctx->ctx_skip == SKIP_UNKNOWN)
{
if (scope->se_u.se_if.is_if_label >= 0)
{
// previous "if" or "elseif" jumps here
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
isn->isn_arg.jump.jump_where = instr->ga_len;
scope->se_u.se_if.is_if_label = -1;
}
}
if (cctx->ctx_skip != SKIP_UNKNOWN)
cctx->ctx_skip = cctx->ctx_skip == SKIP_YES ? SKIP_NOT : SKIP_YES;
}
return p;
}
@ -5797,6 +5871,8 @@ compile_endif(char_u *arg, cctx_T *cctx)
}
ifscope = &scope->se_u.se_if;
unwind_locals(cctx, scope->se_local_count);
if (!cctx->ctx_had_return)
ifscope->is_had_return = FALSE;
if (scope->se_u.se_if.is_if_label >= 0)
{
@ -5806,7 +5882,11 @@ compile_endif(char_u *arg, cctx_T *cctx)
}
// Fill in the "end" label in jumps at the end of the blocks.
compile_fill_jump_to_end(&ifscope->is_end_label, cctx);
cctx->ctx_skip = FALSE;
cctx->ctx_skip = scope->se_skip_save;
// If all the blocks end in :return and there is an :else then the
// had_return flag is set.
cctx->ctx_had_return = ifscope->is_had_return && ifscope->is_seen_else;
drop_scope(cctx);
return arg;
@ -6405,7 +6485,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
char_u *p;
int has_expr = FALSE;
if (cctx->ctx_skip == TRUE)
if (cctx->ctx_skip == SKIP_YES)
goto theend;
if (eap->cmdidx >= 0 && eap->cmdidx < CMD_SIZE)
@ -6473,13 +6553,22 @@ theend:
/*
* Add a function to the list of :def functions.
* This "sets ufunc->uf_dfunc_idx" but the function isn't compiled yet.
* This sets "ufunc->uf_dfunc_idx" but the function isn't compiled yet.
*/
static int
add_def_function(ufunc_T *ufunc)
{
dfunc_T *dfunc;
if (def_functions.ga_len == 0)
{
// The first position is not used, so that a zero uf_dfunc_idx means it
// wasn't set.
if (ga_grow(&def_functions, 1) == FAIL)
return FAIL;
++def_functions.ga_len;
}
// Add the function to "def_functions".
if (ga_grow(&def_functions, 1) == FAIL)
return FAIL;
@ -6509,7 +6598,6 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
char_u *line = NULL;
char_u *p;
char *errormsg = NULL; // error message
int had_return = FALSE;
cctx_T cctx;
garray_T *instr;
int called_emsg_before = called_emsg;
@ -6520,7 +6608,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
// When using a function that was compiled before: Free old instructions.
// Otherwise add a new entry in "def_functions".
if (ufunc->uf_dfunc_idx >= 0)
if (ufunc->uf_dfunc_idx > 0)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx;
@ -6613,7 +6701,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
if (line != NULL && *line == '|')
// the line continues after a '|'
++line;
else if (line != NULL && *line != NUL
else if (line != NULL && *skipwhite(line) != NUL
&& !(*line == '#' && (line == cctx.ctx_line_start
|| VIM_ISWHITE(line[-1]))))
{
@ -6622,14 +6710,13 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
}
else
{
line = next_line_from_context(&cctx);
line = next_line_from_context(&cctx, FALSE);
if (cctx.ctx_lnum >= ufunc->uf_lines.ga_len)
// beyond the last line
break;
}
emsg_before = called_emsg;
had_return = FALSE;
CLEAR_FIELD(ea);
ea.cmdlinep = &line;
ea.cmd = skipwhite(line);
@ -6770,7 +6857,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
if (p == ea.cmd && ea.cmdidx != CMD_SIZE)
{
if (cctx.ctx_skip == TRUE)
if (cctx.ctx_skip == SKIP_YES)
{
line += STRLEN(line);
continue;
@ -6795,7 +6882,8 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
p = skipwhite(p);
if (cctx.ctx_skip == TRUE
if (cctx.ctx_skip == SKIP_YES
&& ea.cmdidx != CMD_if
&& ea.cmdidx != CMD_elseif
&& ea.cmdidx != CMD_else
&& ea.cmdidx != CMD_endif)
@ -6804,6 +6892,22 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
continue;
}
if (ea.cmdidx != CMD_elseif
&& ea.cmdidx != CMD_else
&& ea.cmdidx != CMD_endif
&& ea.cmdidx != CMD_endfor
&& ea.cmdidx != CMD_endwhile
&& ea.cmdidx != CMD_catch
&& ea.cmdidx != CMD_finally
&& ea.cmdidx != CMD_endtry)
{
if (cctx.ctx_had_return)
{
emsg(_("E1095: Unreachable code after :return"));
goto erret;
}
}
switch (ea.cmdidx)
{
case CMD_def:
@ -6817,7 +6921,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
case CMD_return:
line = compile_return(p, set_return_type, &cctx);
had_return = TRUE;
cctx.ctx_had_return = TRUE;
break;
case CMD_let:
@ -6842,9 +6946,11 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
break;
case CMD_elseif:
line = compile_elseif(p, &cctx);
cctx.ctx_had_return = FALSE;
break;
case CMD_else:
line = compile_else(p, &cctx);
cctx.ctx_had_return = FALSE;
break;
case CMD_endif:
line = compile_endif(p, &cctx);
@ -6855,6 +6961,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
break;
case CMD_endwhile:
line = compile_endwhile(p, &cctx);
cctx.ctx_had_return = FALSE;
break;
case CMD_for:
@ -6862,6 +6969,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
break;
case CMD_endfor:
line = compile_endfor(p, &cctx);
cctx.ctx_had_return = FALSE;
break;
case CMD_continue:
line = compile_continue(p, &cctx);
@ -6875,12 +6983,15 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
break;
case CMD_catch:
line = compile_catch(p, &cctx);
cctx.ctx_had_return = FALSE;
break;
case CMD_finally:
line = compile_finally(p, &cctx);
cctx.ctx_had_return = FALSE;
break;
case CMD_endtry:
line = compile_endtry(p, &cctx);
cctx.ctx_had_return = FALSE;
break;
case CMD_throw:
line = compile_throw(p, &cctx);
@ -6925,7 +7036,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
goto erret;
}
if (!had_return)
if (!cctx.ctx_had_return)
{
if (ufunc->uf_ret_type->tt_type != VAR_VOID)
{
@ -6948,6 +7059,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
dfunc->df_closure_count = cctx.ctx_closure_count;
if (cctx.ctx_outer_used)
ufunc->uf_flags |= FC_CLOSURE;
ufunc->uf_def_status = UF_COMPILED;
}
ret = OK;
@ -6967,7 +7079,7 @@ erret:
if (!dfunc->df_deleted
&& ufunc->uf_dfunc_idx == def_functions.ga_len - 1)
--def_functions.ga_len;
ufunc->uf_dfunc_idx = UF_NOT_COMPILED;
ufunc->uf_def_status = UF_NOT_COMPILED;
while (cctx.ctx_scope != NULL)
drop_scope(&cctx);
@ -7195,17 +7307,19 @@ delete_def_function_contents(dfunc_T *dfunc)
}
/*
* When a user function is deleted, delete any associated def function.
* When a user function is deleted, clear the contents of any associated def
* function. The position in def_functions can be re-used.
*/
void
delete_def_function(ufunc_T *ufunc)
clear_def_function(ufunc_T *ufunc)
{
if (ufunc->uf_dfunc_idx >= 0)
if (ufunc->uf_dfunc_idx > 0)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx;
delete_def_function_contents(dfunc);
ufunc->uf_def_status = UF_NOT_COMPILED;
}
}

View File

@ -487,10 +487,10 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
int error;
int idx;
if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED
if (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, FALSE, NULL) == FAIL)
return FAIL;
if (ufunc->uf_dfunc_idx >= 0)
if (ufunc->uf_def_status == UF_COMPILED)
{
// The function has been compiled, can call it quickly. For a function
// that was defined later: we can call it directly next time.
@ -671,8 +671,8 @@ call_def_function(
// Like STACK_TV_VAR but use the outer scope
#define STACK_OUT_TV_VAR(idx) (((typval_T *)ectx.ec_outer_stack->ga_data) + ectx.ec_outer_frame + STACK_FRAME_SIZE + idx)
if (ufunc->uf_dfunc_idx == UF_NOT_COMPILED
|| (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED
if (ufunc->uf_def_status == UF_NOT_COMPILED
|| (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, FALSE, NULL) == FAIL))
{
if (called_emsg == called_emsg_before)
@ -2119,12 +2119,8 @@ call_def_function(
list_T *list;
int count = iptr->isn_arg.number;
// type will have been checked to be a list
tv = STACK_TV_BOT(-1);
if (tv->v_type != VAR_LIST)
{
emsg(_(e_listreq));
goto failed;
}
list = tv->vval.v_list;
// no error for short list, expect it to be checked earlier
@ -2148,18 +2144,10 @@ call_def_function(
listitem_T *li;
int index = iptr->isn_arg.number;
// get list item: list is at stack-1, push item
// Get list item: list is at stack-1, push item.
// List type and length is checked for when compiling.
tv = STACK_TV_BOT(-1);
if (tv->v_type != VAR_LIST)
{
emsg(_(e_listreq));
goto failed;
}
if ((li = list_find(tv->vval.v_list, index)) == NULL)
{
semsg(_(e_listidx), index);
goto failed;
}
li = list_find(tv->vval.v_list, index);
if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
goto failed;
@ -2391,10 +2379,10 @@ ex_disassemble(exarg_T *eap)
semsg(_("E1061: Cannot find function %s"), eap->arg);
return;
}
if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED
if (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, FALSE, NULL) == FAIL)
return;
if (ufunc->uf_dfunc_idx < 0)
if (ufunc->uf_def_status != UF_COMPILED)
{
semsg(_("E1062: Function %s is not compiled"), eap->arg);
return;

View File

@ -32,13 +32,14 @@ in_vim9script(void)
void
ex_vim9script(exarg_T *eap)
{
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
scriptitem_T *si;
if (!getline_equal(eap->getline, eap->cookie, getsourceline))
{
emsg(_("E1038: vim9script can only be used in a script"));
return;
}
si = SCRIPT_ITEM(current_sctx.sc_sid);
if (si->sn_had_command)
{
emsg(_("E1039: vim9script must be the first command in a script"));
@ -141,8 +142,15 @@ free_imports(int sid)
void
ex_import(exarg_T *eap)
{
char_u *cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, NULL);
char_u *cmd_end;
if (!getline_equal(eap->getline, eap->cookie, getsourceline))
{
emsg(_("E1094: import can only be used in a script"));
return;
}
cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, NULL);
if (cmd_end != NULL)
eap->nextcmd = check_nextcmd(cmd_end);
}
@ -463,7 +471,7 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
}
for (p = arg + 1; *p != NUL && eval_isnamec(*p); MB_PTR_ADV(p))
if (*p == ':' && p != arg + 1)
if (*p == ':' && (VIM_ISWHITE(p[1]) || p != arg + 1))
break;
if (*p != ':')
@ -499,7 +507,7 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
/*
* Check if the type of script variable "dest" allows assigning "value".
*/
void
int
check_script_var_type(typval_T *dest, typval_T *value, char_u *name)
{
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
@ -513,13 +521,15 @@ check_script_var_type(typval_T *dest, typval_T *value, char_u *name)
if (sv->sv_tv == dest)
{
if (sv->sv_const)
{
semsg(_(e_readonlyvar), name);
else
check_type(sv->sv_type, typval2type(value), TRUE);
return;
return FAIL;
}
return check_type(sv->sv_type, typval2type(value), TRUE);
}
}
iemsg("check_script_var_type(): not found");
return OK; // not really
}
#endif // FEAT_EVAL