diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d64096e627..f750182422 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -8231,6 +8231,8 @@ prompt_getprompt({buf}) *prompt_getprompt()* Can also be used as a |method|: > GetBuffer()->prompt_getprompt() +< {only available when compiled with the |+channel| feature} + prompt_setcallback({buf}, {expr}) *prompt_setcallback()* Set prompt callback for buffer {buf} to {expr}. When {expr} @@ -8264,6 +8266,7 @@ prompt_setcallback({buf}, {expr}) *prompt_setcallback()* < Can also be used as a |method|: > GetBuffer()->prompt_setcallback(callback) +< {only available when compiled with the |+channel| feature} prompt_setinterrupt({buf}, {expr}) *prompt_setinterrupt()* Set a callback for buffer {buf} to {expr}. When {expr} is an @@ -8277,6 +8280,8 @@ prompt_setinterrupt({buf}, {expr}) *prompt_setinterrupt()* Can also be used as a |method|: > GetBuffer()->prompt_setinterrupt(callback) +< {only available when compiled with the |+channel| feature} + prompt_setprompt({buf}, {text}) *prompt_setprompt()* Set prompt for buffer {buf} to {text}. You most likely want {text} to end in a space. @@ -8287,6 +8292,8 @@ prompt_setprompt({buf}, {text}) *prompt_setprompt()* Can also be used as a |method|: > GetBuffer()->prompt_setprompt('command: ') +< {only available when compiled with the |+channel| feature} + prop_ functions are documented here: |text-prop-functions| pum_getpos() *pum_getpos()* @@ -9890,6 +9897,10 @@ sha256({string}) *sha256()* shellescape({string} [, {special}]) *shellescape()* Escape {string} for use as a shell command argument. + When the 'shell' contains powershell (MS-Windows) or pwsh + (MS-Windows, Linux, and MacOS) then it will enclose {string} + in single quotes and will double up all internal single + quotes. On MS-Windows, when 'shellslash' is not set, it will enclose {string} in double quotes and double all double quotes within {string}. @@ -11052,7 +11063,8 @@ tempname() *tempname()* *temp-file-name* :exe "redir > " . tmpfile < For Unix, the file will be in a private directory |tempfile|. For MS-Windows forward slashes are used when the 'shellslash' - option is set or when 'shellcmdflag' starts with '-'. + option is set, or when 'shellcmdflag' starts with '-' and + 'shell' does not contain powershell or pwsh. term_ functions are documented here: |terminal-function-details| diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 5947feeeac..7f02e5ef81 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6726,23 +6726,25 @@ A jump table for the options with a short description can be found at |Q_op|. *'shellcmdflag'* *'shcf'* 'shellcmdflag' 'shcf' string (default: "-c"; - Win32, when 'shell' does not contain "sh" + Win32, when 'shell' contains "powershell": + "-Command", or when it does not contain "sh" somewhere: "/c") global Flag passed to the shell to execute "!" and ":!" commands; e.g., - "bash.exe -c ls" or "cmd.exe /c dir". For MS-Windows, the default is - set according to the value of 'shell', to reduce the need to set this - option by the user. + "bash.exe -c ls", "powershell.exe -Command dir", or "cmd.exe /c dir". + For MS-Windows, the default is set according to the value of 'shell', + to reduce the need to set this option by the user. On Unix it can have more than one flag. Each white space separated part is passed as an argument to the shell command. See |option-backslash| about including spaces and backslashes. - Also see |dos-shell| for MS-Windows. + Also see |dos-shell| and |dos-powershell| for MS-Windows. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. *'shellpipe'* *'sp'* -'shellpipe' 'sp' string (default ">", ">%s 2>&1", "| tee", "|& tee" or - "2>&1| tee") +'shellpipe' 'sp' string (default ">", ">%s 2>&1", "| tee", "|& tee" + "2>&1| tee", or + "2>&1 | Out-File -Encoding default") global {not available when compiled without the |+quickfix| feature} @@ -6752,9 +6754,10 @@ A jump table for the options with a short description can be found at |Q_op|. The name of the temporary file can be represented by "%s" if necessary (the file name is appended automatically if no %s appears in the value of this option). - For the Amiga the default is ">". For MS-Windows the default is - ">%s 2>&1". The output is directly saved in a file and not echoed to - the screen. + For the Amiga the default is ">". For MS-Windows using powershell the + default is "2>&1 | Out-File -Encoding default", otherwise the default + is ">%s 2>&1". The output is directly saved in a file and not echoed + to the screen. For Unix the default is "| tee". The stdout of the compiler is saved in a file and echoed to the screen. If the 'shell' option is "csh" or "tcsh" after initializations, the default becomes "|& tee". If the @@ -6762,6 +6765,8 @@ A jump table for the options with a short description can be found at |Q_op|. "bash", "fish", "ash" or "dash" the default becomes "2>&1| tee". This means that stderr is also included. Before using the 'shell' option a path is removed, thus "/bin/sh" uses "sh". + For Unix and MS-Windows, when the 'shell' option is "pwsh" the default + becomes ">%s 2>&1" and the output is not echoed to the screen. The initialization of this option is done after reading the ".vimrc" and the other initializations, so that when the 'shell' option is set there, the 'shellpipe' option changes automatically, unless it was @@ -6777,8 +6782,7 @@ A jump table for the options with a short description can be found at |Q_op|. security reasons. *'shellquote'* *'shq'* -'shellquote' 'shq' string (default: ""; Win32, when 'shell' - contains "sh" somewhere: "\"") +'shellquote' 'shq' string (default: "") global Quoting character(s), put around the command passed to the shell, for the "!" and ":!" commands. The redirection is kept outside of the @@ -6786,14 +6790,13 @@ A jump table for the options with a short description can be found at |Q_op|. probably not useful to set both options. This is an empty string by default. Only known to be useful for third-party shells on MS-Windows-like systems, such as the MKS Korn - Shell or bash, where it should be "\"". The default is adjusted - according the value of 'shell', to reduce the need to set this option - by the user. See |dos-shell|. + Shell or bash, where it should be "\"". See |dos-shell|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. *'shellredir'* *'srr'* -'shellredir' 'srr' string (default ">", ">&" or ">%s 2>&1") +'shellredir' 'srr' string (default ">", ">&", ">%s 2>&1", or + "2>&1 | Out-File -Encoding default") global String to be used to put the output of a filter command in a temporary file. See also |:!|. See |option-backslash| about including spaces @@ -6804,10 +6807,12 @@ A jump table for the options with a short description can be found at |Q_op|. The default is ">". For Unix, if the 'shell' option is "csh" or "tcsh" during initializations, the default becomes ">&". If the 'shell' option is "sh", "ksh", "mksh", "pdksh", "zsh", "zsh-beta", - "bash" or "fish", the default becomes ">%s 2>&1". This means that - stderr is also included. For Win32, the Unix checks are done and - additionally "cmd" is checked for, which makes the default ">%s 2>&1". - Also, the same names with ".exe" appended are checked for. + "bash", "fish", or "pwsh", the default becomes ">%s 2>&1". This means + that stderr is also included. For Win32, the Unix checks are done and + additionally "cmd" is checked for, which makes the default ">%s 2>&1", + and "powershell" is checked for which makes the default + "2>&1 | Out-File -Encoding default" (see |dos-powershell|). Also, the + same names with ".exe" appended are checked for. The initialization of this option is done after reading the ".vimrc" and the other initializations, so that when the 'shell' option is set there, the 'shellredir' option changes automatically unless it was @@ -6822,9 +6827,9 @@ A jump table for the options with a short description can be found at |Q_op|. global {only for MS-Windows} When set, a forward slash is used when expanding file names. This is - useful when a Unix-like shell is used instead of cmd.exe. Backward - slashes can still be typed, but they are changed to forward slashes by - Vim. + useful when a Unix-like shell is used instead of cmd.exe, pwsh.exe, or + powershell.exe. Backward slashes can still be typed, but they are + changed to forward slashes by Vim. Note that setting or resetting this option has no effect for some existing file names, thus this option needs to be set before opening any file for best results. This might change in the future. @@ -6878,6 +6883,8 @@ A jump table for the options with a short description can be found at |Q_op|. *'shellxquote'* *'sxq'* 'shellxquote' 'sxq' string (default: ""; for Win32, when 'shell' is cmd.exe: "(" + for Win32, when 'shell' is + powershell.exe: "\"" for Win32, when 'shell' contains "sh" somewhere: "\"" for Unix, when using system(): "\"") @@ -6890,11 +6897,12 @@ A jump table for the options with a short description can be found at |Q_op|. then ')"' is appended. When the value is '(' then also see 'shellxescape'. This is an empty string by default on most systems, but is known to be - useful for on Win32 version, either for cmd.exe which automatically - strips off the first and last quote on a command, or 3rd-party shells - such as the MKS Korn Shell or bash, where it should be "\"". The - default is adjusted according the value of 'shell', to reduce the need - to set this option by the user. See |dos-shell|. + useful for on Win32 version, either for cmd.exe, powershell.exe, or + pwsh.exe which automatically strips off the first and last quote on a + command, or 3rd-party shells such as the MKS Korn Shell or bash, where + it should be "\"". The default is adjusted according the value of + 'shell', to reduce the need to set this option by the user. See + |dos-shell|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. diff --git a/runtime/doc/os_dos.txt b/runtime/doc/os_dos.txt index 925c7e0915..b473134ce1 100644 --- a/runtime/doc/os_dos.txt +++ b/runtime/doc/os_dos.txt @@ -17,6 +17,7 @@ versions of Vim. Also see |os_win32.txt| and |os_msdos.txt|. 7. Interrupting |dos-CTRL-Break| 8. Temp files |dos-temp-files| 9. Shell option default |dos-shell| +10. PowerShell |dos-powershell| ============================================================================== 1. File locations *dos-locations* @@ -297,8 +298,86 @@ For Win32 as: -c "command name >file" For DOS 32 bit, DJGPP does this internally somehow. -When starting up, Vim checks for the presence of "sh" anywhere in the 'shell' -option. If it is present, Vim sets the 'shellcmdflag' and 'shellquote' or -'shellxquote' options will be set as described above. +When starting up, if Vim does not recognise a standard Windows shell it checks +for the presence of "sh" anywhere in the 'shell' option. If it is present, +Vim sets the 'shellcmdflag' and 'shellquote' or 'shellxquote' options will be +set as described above. + +============================================================================== +10. PowerShell *dos-powershell* *dos-pwsh* + +Vim supports PowerShell Desktop and PowerShell Core. PowerShell Desktop is +the version of PowerShell that is installed with Windows, while PowerShell +Core is a separate downloadable version that works cross-platform. To see +which version you are using then enter the following in a PowerShell prompt - +$PSVersionTable.PSEdition + +If 'shell' includes "powershell" in the filename at startup then VIM sets +'shellcmdflag', 'shellxquote', 'shellpipe', and 'shellredir' options to the +following values: + +'shellcmdflag' -Command +'shellxquote' " +'shellpipe' 2>&1 | Out-File -Encoding default +'shellredir' 2>&1 | Out-File -Encoding default + +If 'shell' includes "pwsh" in the filename at startup then VIM sets +'shellcmdflag', 'shellxquote', 'shellpipe', and 'shellredir' options to the +following values: + +'shellcmdflag' -c +'shellxquote' " +'shellpipe' >%s 2>&1 +'shellredir' >%s 2>&1 + +If you find that PowerShell commands are taking a long time to run then try +with "-NoProfile" at the beginning of the 'shellcmdflag'. Note this will +prevent any PowerShell environment setup by the profile from taking place. + +If you have problems running PowerShell scripts through the 'shell' then try +with "-ExecutionPolicy RemoteSigned -Command" at the beginning of +'shellcmdflag'. See online Windows documentation for more information on +PowerShell Execution Policy settings. + +See |option-backslash| about including spaces in 'shellcmdflag' when using +multiple flags. + +The 'shellpipe' and 'shellredir' option values re-encode the UTF-16le output +from PowerShell Desktop to your currently configured console codepage. The +output can be forced into a different encoding by changing "default" to one of +the following: + + unicode - UTF-16le (default output from PowerShell 5.1) + bigendianunicode - UTF-16 + utf8 - UTF-8 + utf7 - UTF-7 (no BOM) + utf32 - UTF-32 + ascii - 7-bit ASCII character set + default - System's active code page (typically ANSI) + oem - System's current OEM code page + +Note The abovce multi-byte Unicode encodings include a leading BOM unless +otherwise indicated. + +By default PowerShell Core's output is UTF-8 encoded without a BOM. If you +want to force the output of PowerShell Core into a different encoding then set +'shellredir' and 'shellpipe' to "2>&1 | Out-File -Encoding encoding" where +encoding is one of the following: + + ascii - 7-bit ASCII character set + bigendianunicode - UTF-16be + bigendianutf32 - UTF-32be + oem - System's current OEM code page + unicode - UTF-16le + utf7 - UTF-7 + utf8 - UTF-8 + utf8BOM - UTF-8, with BOM + utf8NoBOM - UTF-8, no BOM (default output from PowerShell Core) + utf32 - UTF-32 + +Since PowerShell Core 6.2, the Encoding parameter also supports specifying a +numeric ID of a registered code page (-Encoding 1251) or string names of +registered code pages (-Encoding "windows-1251"). The .NET documentation for +Encoding.CodePage has more information vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/os_win32.txt b/runtime/doc/os_win32.txt index 6c366083f6..3767cb8a98 100644 --- a/runtime/doc/os_win32.txt +++ b/runtime/doc/os_win32.txt @@ -30,6 +30,7 @@ File formats |dos-file-formats| Interrupting |dos-CTRL-Break| Temp files |dos-temp-files| Shell option default |dos-shell| +PowerShell defaults |dos-powershell| Win32 GUI |gui-w32| diff --git a/runtime/filetype.vim b/runtime/filetype.vim index e38bc23de0..2cb3f0b1d0 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -650,6 +650,9 @@ au BufNewFile,BufRead *.mo,*.gdmo setf gdmo " Gedcom au BufNewFile,BufRead *.ged,lltxxxxx.txt setf gedcom +" Gemtext +au BufNewFile,BufRead *.gmi,*.gemini setf gemtext + " Gift (Moodle) autocmd BufRead,BufNewFile *.gift setf gift @@ -868,6 +871,9 @@ au BufNewFile,BufRead *.json-patch setf json " Jupyter Notebook is also json au BufNewFile,BufRead *.ipynb setf json +" JSONC +au BufNewFile,BufRead *.jsonc setf jsonc + " Kixtart au BufNewFile,BufRead *.kix setf kix diff --git a/src/buffer.c b/src/buffer.c index 3b6c26fbf1..42bf3e0583 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1134,7 +1134,12 @@ handle_swap_exists(bufref_T *old_curbuf) close_buffer(curwin, curbuf, DOBUF_UNLOAD, FALSE, FALSE); if (old_curbuf == NULL || !bufref_valid(old_curbuf) || old_curbuf->br_buf == curbuf) + { + // Block autocommands here because curwin->w_buffer is NULL. + block_autocmds(); buf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); + unblock_autocmds(); + } else buf = old_curbuf->br_buf; if (buf != NULL) diff --git a/src/channel.c b/src/channel.c index 643d239775..03abdac7bd 100644 --- a/src/channel.c +++ b/src/channel.c @@ -2512,12 +2512,17 @@ channel_exe_cmd(channel_T *channel, ch_part_T part, typval_T *argv) if (STRCMP(cmd, "ex") == 0) { - int called_emsg_before = called_emsg; + int called_emsg_before = called_emsg; + char_u *p = arg; + int do_emsg_silent; ch_log(channel, "Executing ex command '%s'", (char *)arg); - ++emsg_silent; + do_emsg_silent = !checkforcmd(&p, "echoerr", 5); + if (do_emsg_silent) + ++emsg_silent; do_cmdline_cmd(arg); - --emsg_silent; + if (do_emsg_silent) + --emsg_silent; if (called_emsg > called_emsg_before) ch_log(channel, "Ex command error: '%s'", (char *)get_vim_var_str(VV_ERRMSG)); @@ -2571,7 +2576,8 @@ channel_exe_cmd(channel_T *channel, ch_part_T part, typval_T *argv) char_u *json = NULL; // Don't pollute the display with errors. - ++emsg_skip; + // Do generate the errors so that try/catch works. + ++emsg_silent; if (!is_call) { ch_log(channel, "Evaluating expression '%s'", (char *)arg); @@ -2607,7 +2613,7 @@ channel_exe_cmd(channel_T *channel, ch_part_T part, typval_T *argv) vim_free(json); } } - --emsg_skip; + --emsg_silent; if (tv == &res_tv) clear_tv(tv); else diff --git a/src/drawscreen.c b/src/drawscreen.c index 96fdee574f..415a086fe5 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -1379,6 +1379,12 @@ fold_line( curwin->w_cline_folded = TRUE; curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW); } + +# ifdef FEAT_CONCEAL + // When the line was not folded w_wrow may have been set, recompute it. + if (wp == curwin && lnum == wp->w_cursor.lnum && conceal_cursor_line(wp)) + curs_columns(TRUE); +# endif } #endif @@ -2003,12 +2009,41 @@ win_update(win_T *wp) ve_flags = VE_ALL; #endif getvcols(wp, &VIsual, &curwin->w_cursor, &fromc, &toc); + ++toc; #if defined(FEAT_LINEBREAK) ve_flags = save_ve_flags; #endif - ++toc; + // Highlight to the end of the line, unless 'virtualedit' has + // "block". if (curwin->w_curswant == MAXCOL) - toc = MAXCOL; + { + if (ve_flags & VE_BLOCK) + { + pos_T pos; + int cursor_above = + curwin->w_cursor.lnum < VIsual.lnum; + + // Need to find the longest line. + toc = 0; + pos.coladd = 0; + for (pos.lnum = curwin->w_cursor.lnum; cursor_above + ? pos.lnum <= VIsual.lnum + : pos.lnum >= VIsual.lnum; + pos.lnum += cursor_above ? 1 : -1) + { + colnr_T t; + + pos.col = STRLEN(ml_get_buf(wp->w_buffer, + pos.lnum, FALSE)); + getvvcol(wp, &pos, NULL, NULL, &t); + if (toc < t) + toc = t; + } + ++toc; + } + else + toc = MAXCOL; + } if (fromc != wp->w_old_cursor_fcol || toc != wp->w_old_cursor_lcol) diff --git a/src/edit.c b/src/edit.c index d331b48a32..2355d862b8 100644 --- a/src/edit.c +++ b/src/edit.c @@ -147,6 +147,9 @@ edit( #ifdef FEAT_JOB_CHANNEL int cmdchar_todo = cmdchar; #endif +#ifdef FEAT_CONCEAL + int cursor_line_was_concealed; +#endif // Remember whether editing was restarted after CTRL-O. did_restart_edit = restart_edit; @@ -222,9 +225,9 @@ edit( } #ifdef FEAT_CONCEAL - // Check if the cursor line needs redrawing before changing State. If - // 'concealcursor' is "n" it needs to be redrawn without concealing. - conceal_check_cursor_line(); + // Check if the cursor line was concealed before changing State. + cursor_line_was_concealed = curwin->w_p_cole > 0 + && conceal_cursor_line(curwin); #endif /* @@ -283,6 +286,12 @@ edit( stop_insert_mode = FALSE; +#ifdef FEAT_CONCEAL + // Check if the cursor line needs redrawing after changing State. If + // 'concealcursor' is "n" it needs to be redrawn without concealing. + conceal_check_cursor_line(cursor_line_was_concealed); +#endif + /* * Need to recompute the cursor position, it might move when the cursor is * on a TAB or special character. diff --git a/src/evalfunc.c b/src/evalfunc.c index b8b4291d77..0ec049d003 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -267,6 +267,33 @@ arg_number(type_T *type, argcontext_T *context) return check_arg_type(&t_number, type, context); } +/* + * Check "type" is a dict of 'any'. + */ + static int +arg_dict_any(type_T *type, argcontext_T *context) +{ + return check_arg_type(&t_dict_any, type, context); +} + +/* + * Check "type" is a list of numbers. + */ + static int +arg_list_number(type_T *type, argcontext_T *context) +{ + return check_arg_type(&t_list_number, type, context); +} + +/* + * Check "type" is a list of strings. + */ + static int +arg_list_string(type_T *type, argcontext_T *context) +{ + return check_arg_type(&t_list_string, type, context); +} + /* * Check "type" is a string. */ @@ -301,6 +328,18 @@ arg_list_or_blob(type_T *type, argcontext_T *context) return FAIL; } +/* + * Check "type" is a string or a number + */ + static int +arg_string_or_nr(type_T *type, argcontext_T *context) +{ + if (type->tt_type == VAR_ANY + || type->tt_type == VAR_STRING || type->tt_type == VAR_NUMBER) + return OK; + arg_type_mismatch(&t_string, type, context->arg_idx + 1); + return FAIL; +} /* * Check "type" is a string or a list of strings. */ @@ -404,14 +443,22 @@ arg_extend3(type_T *type, argcontext_T *context) */ argcheck_T arg1_string[] = {arg_string}; argcheck_T arg1_number[] = {arg_number}; +argcheck_T arg1_dict[] = {arg_dict_any}; +argcheck_T arg1_list_number[] = {arg_list_number}; +argcheck_T arg1_string_list[] = {arg_list_string}; argcheck_T arg1_float_or_nr[] = {arg_float_or_nr}; +argcheck_T arg1_string_or_nr[] = {arg_string_or_nr}; +argcheck_T arg1_string_or_list[] = {arg_string_or_list}; argcheck_T arg2_float_or_nr[] = {arg_float_or_nr, arg_float_or_nr}; argcheck_T arg2_number[] = {arg_number, arg_number}; +argcheck_T arg2_string[] = {arg_string, arg_string}; +argcheck_T arg2_list_number[] = {arg_list_number, arg_list_number}; argcheck_T arg2_listblob_item[] = {arg_list_or_blob, arg_item_of_prev}; argcheck_T arg2_execute[] = {arg_string_or_list, arg_string}; argcheck_T arg23_extend[] = {arg_list_or_dict, arg_same_as_prev, arg_extend3}; argcheck_T arg23_extendnew[] = {arg_list_or_dict, arg_same_struct_as_prev, arg_extend3}; argcheck_T arg3_string[] = {arg_string, arg_string, arg_string}; +argcheck_T arg3_number[] = {arg_number, arg_number, arg_number}; argcheck_T arg3_string_nr_bool[] = {arg_string, arg_number, arg_bool}; argcheck_T arg3_insert[] = {arg_list_or_blob, arg_item_of_prev, arg_number}; @@ -732,7 +779,7 @@ static funcentry_T global_functions[] = NULL #endif }, - {"balloon_show", 1, 1, FEARG_1, NULL, + {"balloon_show", 1, 1, FEARG_1, arg1_string_or_list, ret_void, #ifdef FEAT_BEVAL f_balloon_show @@ -740,7 +787,7 @@ static funcentry_T global_functions[] = NULL #endif }, - {"balloon_split", 1, 1, FEARG_1, NULL, + {"balloon_split", 1, 1, FEARG_1, arg1_string, ret_list_string, #if defined(FEAT_BEVAL_TERM) f_balloon_split @@ -752,31 +799,31 @@ static funcentry_T global_functions[] = ret_string, f_browse}, {"browsedir", 2, 2, 0, NULL, ret_string, f_browsedir}, - {"bufadd", 1, 1, FEARG_1, NULL, + {"bufadd", 1, 1, FEARG_1, arg1_string, ret_number, f_bufadd}, - {"bufexists", 1, 1, FEARG_1, NULL, + {"bufexists", 1, 1, FEARG_1, arg1_string_or_nr, ret_number_bool, f_bufexists}, - {"buffer_exists", 1, 1, FEARG_1, NULL, // obsolete + {"buffer_exists", 1, 1, FEARG_1, arg1_string_or_nr, // obsolete ret_number_bool, f_bufexists}, - {"buffer_name", 0, 1, FEARG_1, NULL, // obsolete + {"buffer_name", 0, 1, FEARG_1, arg1_string_or_nr, // obsolete ret_string, f_bufname}, {"buffer_number", 0, 1, FEARG_1, NULL, // obsolete ret_number, f_bufnr}, - {"buflisted", 1, 1, FEARG_1, NULL, + {"buflisted", 1, 1, FEARG_1, arg1_string_or_nr, ret_number_bool, f_buflisted}, - {"bufload", 1, 1, FEARG_1, NULL, + {"bufload", 1, 1, FEARG_1, arg1_string_or_nr, ret_void, f_bufload}, - {"bufloaded", 1, 1, FEARG_1, NULL, + {"bufloaded", 1, 1, FEARG_1, arg1_string_or_nr, ret_number_bool, f_bufloaded}, - {"bufname", 0, 1, FEARG_1, NULL, + {"bufname", 0, 1, FEARG_1, arg1_string_or_nr, ret_string, f_bufname}, {"bufnr", 0, 2, FEARG_1, NULL, ret_number, f_bufnr}, - {"bufwinid", 1, 1, FEARG_1, NULL, + {"bufwinid", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_bufwinid}, - {"bufwinnr", 1, 1, FEARG_1, NULL, + {"bufwinnr", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_bufwinnr}, - {"byte2line", 1, 1, FEARG_1, NULL, + {"byte2line", 1, 1, FEARG_1, arg1_number, ret_number, f_byte2line}, {"byteidx", 2, 2, FEARG_1, NULL, ret_number, f_byteidx}, @@ -826,15 +873,15 @@ static funcentry_T global_functions[] = ret_number, f_changenr}, {"char2nr", 1, 2, FEARG_1, NULL, ret_number, f_char2nr}, - {"charclass", 1, 1, FEARG_1, NULL, + {"charclass", 1, 1, FEARG_1, arg1_string, ret_number, f_charclass}, {"charcol", 1, 1, FEARG_1, NULL, ret_number, f_charcol}, {"charidx", 2, 3, FEARG_1, NULL, ret_number, f_charidx}, - {"chdir", 1, 1, FEARG_1, NULL, + {"chdir", 1, 1, FEARG_1, arg1_string, ret_string, f_chdir}, - {"cindent", 1, 1, FEARG_1, NULL, + {"cindent", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_cindent}, {"clearmatches", 0, 1, FEARG_1, arg1_number, ret_void, f_clearmatches}, @@ -846,7 +893,7 @@ static funcentry_T global_functions[] = ret_number, f_complete_add}, {"complete_check", 0, 0, 0, NULL, ret_number_bool, f_complete_check}, - {"complete_info", 0, 1, FEARG_1, NULL, + {"complete_info", 0, 1, FEARG_1, arg1_string_list, ret_dict_any, f_complete_info}, {"confirm", 1, 4, FEARG_1, NULL, ret_number, f_confirm}, @@ -878,7 +925,7 @@ static funcentry_T global_functions[] = ret_number_bool, f_deletebufline}, {"did_filetype", 0, 0, 0, NULL, ret_number_bool, f_did_filetype}, - {"diff_filler", 1, 1, FEARG_1, NULL, + {"diff_filler", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_diff_filler}, {"diff_hlID", 2, 2, FEARG_1, NULL, ret_number, f_diff_hlID}, @@ -888,37 +935,37 @@ static funcentry_T global_functions[] = ret_number_bool, f_empty}, {"environ", 0, 0, 0, NULL, ret_dict_string, f_environ}, - {"escape", 2, 2, FEARG_1, NULL, + {"escape", 2, 2, FEARG_1, arg2_string, ret_string, f_escape}, - {"eval", 1, 1, FEARG_1, NULL, + {"eval", 1, 1, FEARG_1, arg1_string, ret_any, f_eval}, {"eventhandler", 0, 0, 0, NULL, ret_number_bool, f_eventhandler}, - {"executable", 1, 1, FEARG_1, NULL, + {"executable", 1, 1, FEARG_1, arg1_string, ret_number, f_executable}, {"execute", 1, 2, FEARG_1, arg2_execute, ret_string, f_execute}, - {"exepath", 1, 1, FEARG_1, NULL, + {"exepath", 1, 1, FEARG_1, arg1_string, ret_string, f_exepath}, - {"exists", 1, 1, FEARG_1, NULL, + {"exists", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_exists}, {"exp", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_exp)}, {"expand", 1, 3, FEARG_1, NULL, ret_any, f_expand}, - {"expandcmd", 1, 1, FEARG_1, NULL, + {"expandcmd", 1, 1, FEARG_1, arg1_string, ret_string, f_expandcmd}, {"extend", 2, 3, FEARG_1, arg23_extend, ret_first_arg, f_extend}, {"extendnew", 2, 3, FEARG_1, arg23_extendnew, ret_first_cont, f_extendnew}, - {"feedkeys", 1, 2, FEARG_1, NULL, + {"feedkeys", 1, 2, FEARG_1, arg2_string, ret_void, f_feedkeys}, - {"file_readable", 1, 1, FEARG_1, NULL, // obsolete + {"file_readable", 1, 1, FEARG_1, arg1_string, // obsolete ret_number_bool, f_filereadable}, - {"filereadable", 1, 1, FEARG_1, NULL, + {"filereadable", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_filereadable}, - {"filewritable", 1, 1, FEARG_1, NULL, + {"filewritable", 1, 1, FEARG_1, arg1_string, ret_number, f_filewritable}, {"filter", 2, 2, FEARG_1, NULL, ret_first_arg, f_filter}, @@ -936,19 +983,19 @@ static funcentry_T global_functions[] = ret_float, FLOAT_FUNC(f_floor)}, {"fmod", 2, 2, FEARG_1, arg2_float_or_nr, ret_float, FLOAT_FUNC(f_fmod)}, - {"fnameescape", 1, 1, FEARG_1, NULL, + {"fnameescape", 1, 1, FEARG_1, arg1_string, ret_string, f_fnameescape}, - {"fnamemodify", 2, 2, FEARG_1, NULL, + {"fnamemodify", 2, 2, FEARG_1, arg2_string, ret_string, f_fnamemodify}, - {"foldclosed", 1, 1, FEARG_1, NULL, + {"foldclosed", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_foldclosed}, - {"foldclosedend", 1, 1, FEARG_1, NULL, + {"foldclosedend", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_foldclosedend}, - {"foldlevel", 1, 1, FEARG_1, NULL, + {"foldlevel", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_foldlevel}, {"foldtext", 0, 0, 0, NULL, ret_string, f_foldtext}, - {"foldtextresult", 1, 1, FEARG_1, NULL, + {"foldtextresult", 1, 1, FEARG_1, arg1_string_or_nr, ret_string, f_foldtextresult}, {"foreground", 0, 0, 0, NULL, ret_void, f_foreground}, @@ -968,7 +1015,7 @@ static funcentry_T global_functions[] = ret_list_string, f_getbufline}, {"getbufvar", 2, 3, FEARG_1, NULL, ret_any, f_getbufvar}, - {"getchangelist", 0, 1, FEARG_1, NULL, + {"getchangelist", 0, 1, FEARG_1, arg1_string_or_nr, ret_list_any, f_getchangelist}, {"getchar", 0, 1, 0, NULL, ret_any, f_getchar}, @@ -996,17 +1043,17 @@ static funcentry_T global_functions[] = ret_list_number, f_getcursorcharpos}, {"getcwd", 0, 2, FEARG_1, arg2_number, ret_string, f_getcwd}, - {"getenv", 1, 1, FEARG_1, NULL, + {"getenv", 1, 1, FEARG_1, arg1_string, ret_any, f_getenv}, - {"getfontname", 0, 1, 0, NULL, + {"getfontname", 0, 1, 0, arg1_string, ret_string, f_getfontname}, - {"getfperm", 1, 1, FEARG_1, NULL, + {"getfperm", 1, 1, FEARG_1, arg1_string, ret_string, f_getfperm}, - {"getfsize", 1, 1, FEARG_1, NULL, + {"getfsize", 1, 1, FEARG_1, arg1_string, ret_number, f_getfsize}, - {"getftime", 1, 1, FEARG_1, NULL, + {"getftime", 1, 1, FEARG_1, arg1_string, ret_number, f_getftime}, - {"getftype", 1, 1, FEARG_1, NULL, + {"getftype", 1, 1, FEARG_1, arg1_string, ret_string, f_getftype}, {"getimstatus", 0, 0, 0, NULL, ret_number_bool, f_getimstatus}, @@ -1016,7 +1063,7 @@ static funcentry_T global_functions[] = ret_f_getline, f_getline}, {"getloclist", 1, 2, 0, NULL, ret_list_or_dict_1, f_getloclist}, - {"getmarklist", 0, 1, FEARG_1, NULL, + {"getmarklist", 0, 1, FEARG_1, arg1_string_or_nr, ret_list_dict_any, f_getmarklist}, {"getmatches", 0, 1, 0, arg1_number, ret_list_dict_any, f_getmatches}, @@ -1024,15 +1071,15 @@ static funcentry_T global_functions[] = ret_dict_number, f_getmousepos}, {"getpid", 0, 0, 0, NULL, ret_number, f_getpid}, - {"getpos", 1, 1, FEARG_1, NULL, + {"getpos", 1, 1, FEARG_1, arg1_string, ret_list_number, f_getpos}, - {"getqflist", 0, 1, 0, NULL, + {"getqflist", 0, 1, 0, arg1_dict, ret_list_or_dict_0, f_getqflist}, {"getreg", 0, 3, FEARG_1, NULL, ret_getreg, f_getreg}, - {"getreginfo", 0, 1, FEARG_1, NULL, + {"getreginfo", 0, 1, FEARG_1, arg1_string, ret_dict_any, f_getreginfo}, - {"getregtype", 0, 1, FEARG_1, NULL, + {"getregtype", 0, 1, FEARG_1, arg1_string, ret_string, f_getregtype}, {"gettabinfo", 0, 1, FEARG_1, arg1_number, ret_list_dict_any, f_gettabinfo}, @@ -1042,7 +1089,7 @@ static funcentry_T global_functions[] = ret_any, f_gettabwinvar}, {"gettagstack", 0, 1, FEARG_1, arg1_number, ret_dict_any, f_gettagstack}, - {"gettext", 1, 1, FEARG_1, NULL, + {"gettext", 1, 1, FEARG_1, arg1_string, ret_string, f_gettext}, {"getwininfo", 0, 1, FEARG_1, arg1_number, ret_list_dict_any, f_getwininfo}, @@ -1056,7 +1103,7 @@ static funcentry_T global_functions[] = ret_any, f_getwinvar}, {"glob", 1, 4, FEARG_1, NULL, ret_any, f_glob}, - {"glob2regpat", 1, 1, FEARG_1, NULL, + {"glob2regpat", 1, 1, FEARG_1, arg1_string, ret_string, f_glob2regpat}, {"globpath", 2, 5, FEARG_2, NULL, ret_any, f_globpath}, @@ -1072,21 +1119,21 @@ static funcentry_T global_functions[] = ret_number, f_hlID}, {"highlight_exists",1, 1, FEARG_1, NULL, // obsolete ret_number_bool, f_hlexists}, - {"histadd", 2, 2, FEARG_2, NULL, + {"histadd", 2, 2, FEARG_2, arg2_string, ret_number_bool, f_histadd}, {"histdel", 1, 2, FEARG_1, NULL, ret_number_bool, f_histdel}, {"histget", 1, 2, FEARG_1, NULL, ret_string, f_histget}, - {"histnr", 1, 1, FEARG_1, NULL, + {"histnr", 1, 1, FEARG_1, arg1_string, ret_number, f_histnr}, - {"hlID", 1, 1, FEARG_1, NULL, + {"hlID", 1, 1, FEARG_1, arg1_string, ret_number, f_hlID}, - {"hlexists", 1, 1, FEARG_1, NULL, + {"hlexists", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_hlexists}, {"hostname", 0, 0, 0, NULL, ret_string, f_hostname}, - {"iconv", 3, 3, FEARG_1, NULL, + {"iconv", 3, 3, FEARG_1, arg3_string, ret_string, f_iconv}, {"indent", 1, 1, FEARG_1, NULL, ret_number, f_indent}, @@ -1096,13 +1143,13 @@ static funcentry_T global_functions[] = ret_string, f_input}, {"inputdialog", 1, 3, FEARG_1, NULL, ret_string, f_inputdialog}, - {"inputlist", 1, 1, FEARG_1, NULL, + {"inputlist", 1, 1, FEARG_1, arg1_string_list, ret_number, f_inputlist}, {"inputrestore", 0, 0, 0, NULL, ret_number_bool, f_inputrestore}, {"inputsave", 0, 0, 0, NULL, ret_number_bool, f_inputsave}, - {"inputsecret", 1, 2, FEARG_1, NULL, + {"inputsecret", 1, 2, FEARG_1, arg2_string, ret_string, f_inputsecret}, {"insert", 2, 3, FEARG_1, arg3_insert, ret_first_arg, f_insert}, @@ -1110,7 +1157,7 @@ static funcentry_T global_functions[] = ret_void, f_interrupt}, {"invert", 1, 1, FEARG_1, arg1_number, ret_number, f_invert}, - {"isdirectory", 1, 1, FEARG_1, NULL, + {"isdirectory", 1, 1, FEARG_1, arg1_string, ret_number_bool, f_isdirectory}, {"isinf", 1, 1, FEARG_1, arg1_float_or_nr, ret_number, MATH_FUNC(f_isinf)}, @@ -1118,7 +1165,7 @@ static funcentry_T global_functions[] = ret_number_bool, f_islocked}, {"isnan", 1, 1, FEARG_1, arg1_float_or_nr, ret_number_bool, MATH_FUNC(f_isnan)}, - {"items", 1, 1, FEARG_1, NULL, + {"items", 1, 1, FEARG_1, arg1_dict, ret_list_any, f_items}, {"job_getchannel", 1, 1, FEARG_1, NULL, ret_channel, JOB_FUNC(f_job_getchannel)}, @@ -1134,15 +1181,15 @@ static funcentry_T global_functions[] = ret_number_bool, JOB_FUNC(f_job_stop)}, {"join", 1, 2, FEARG_1, NULL, ret_string, f_join}, - {"js_decode", 1, 1, FEARG_1, NULL, + {"js_decode", 1, 1, FEARG_1, arg1_string, ret_any, f_js_decode}, {"js_encode", 1, 1, FEARG_1, NULL, ret_string, f_js_encode}, - {"json_decode", 1, 1, FEARG_1, NULL, + {"json_decode", 1, 1, FEARG_1, arg1_string, ret_any, f_json_decode}, {"json_encode", 1, 1, FEARG_1, NULL, ret_string, f_json_encode}, - {"keys", 1, 1, FEARG_1, NULL, + {"keys", 1, 1, FEARG_1, arg1_dict, ret_list_string, f_keys}, {"last_buffer_nr", 0, 0, 0, NULL, // obsolete ret_number, f_last_buffer_nr}, @@ -1154,9 +1201,9 @@ static funcentry_T global_functions[] = ret_number, f_libcallnr}, {"line", 1, 2, FEARG_1, NULL, ret_number, f_line}, - {"line2byte", 1, 1, FEARG_1, NULL, + {"line2byte", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_line2byte}, - {"lispindent", 1, 1, FEARG_1, NULL, + {"lispindent", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_lispindent}, {"list2str", 1, 2, FEARG_1, NULL, ret_string, f_list2str}, @@ -1214,7 +1261,7 @@ static funcentry_T global_functions[] = ret_list_any, f_matchstrpos}, {"max", 1, 1, FEARG_1, NULL, ret_number, f_max}, - {"menu_info", 1, 2, FEARG_1, NULL, + {"menu_info", 1, 2, FEARG_1, arg2_string, ret_dict_any, #ifdef FEAT_MENU f_menu_info @@ -1236,7 +1283,7 @@ static funcentry_T global_functions[] = NULL #endif }, - {"nextnonblank", 1, 1, FEARG_1, NULL, + {"nextnonblank", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_nextnonblank}, {"nr2char", 1, 2, FEARG_1, NULL, ret_string, f_nr2char}, @@ -1296,11 +1343,11 @@ static funcentry_T global_functions[] = ret_void, PROP_FUNC(f_popup_show)}, {"pow", 2, 2, FEARG_1, arg2_float_or_nr, ret_float, FLOAT_FUNC(f_pow)}, - {"prevnonblank", 1, 1, FEARG_1, NULL, + {"prevnonblank", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_prevnonblank}, {"printf", 1, 19, FEARG_2, NULL, ret_string, f_printf}, - {"prompt_getprompt", 1, 1, FEARG_1, NULL, + {"prompt_getprompt", 1, 1, FEARG_1, arg1_string_or_nr, ret_string, JOB_FUNC(f_prompt_getprompt)}, {"prompt_setcallback", 2, 2, FEARG_1, NULL, ret_void, JOB_FUNC(f_prompt_setcallback)}, @@ -1356,11 +1403,11 @@ static funcentry_T global_functions[] = NULL #endif }, - {"rand", 0, 1, FEARG_1, NULL, + {"rand", 0, 1, FEARG_1, arg1_list_number, ret_number, f_rand}, {"range", 1, 3, FEARG_1, NULL, ret_list_number, f_range}, - {"readblob", 1, 1, FEARG_1, NULL, + {"readblob", 1, 1, FEARG_1, arg1_string, ret_blob, f_readblob}, {"readdir", 1, 3, FEARG_1, NULL, ret_list_string, f_readdir}, @@ -1374,15 +1421,15 @@ static funcentry_T global_functions[] = ret_string, f_reg_executing}, {"reg_recording", 0, 0, 0, NULL, ret_string, f_reg_recording}, - {"reltime", 0, 2, FEARG_1, NULL, + {"reltime", 0, 2, FEARG_1, arg2_list_number, ret_list_any, f_reltime}, - {"reltimefloat", 1, 1, FEARG_1, NULL, + {"reltimefloat", 1, 1, FEARG_1, arg1_list_number, ret_float, FLOAT_FUNC(f_reltimefloat)}, - {"reltimestr", 1, 1, FEARG_1, NULL, + {"reltimestr", 1, 1, FEARG_1, arg1_list_number, ret_string, f_reltimestr}, {"remote_expr", 2, 4, FEARG_1, NULL, ret_string, f_remote_expr}, - {"remote_foreground", 1, 1, FEARG_1, NULL, + {"remote_foreground", 1, 1, FEARG_1, arg1_string, ret_string, f_remote_foreground}, {"remote_peek", 1, 2, FEARG_1, NULL, ret_number, f_remote_peek}, @@ -1390,15 +1437,15 @@ static funcentry_T global_functions[] = ret_string, f_remote_read}, {"remote_send", 2, 3, FEARG_1, NULL, ret_string, f_remote_send}, - {"remote_startserver", 1, 1, FEARG_1, NULL, + {"remote_startserver", 1, 1, FEARG_1, arg1_string, ret_void, f_remote_startserver}, {"remove", 2, 3, FEARG_1, NULL, ret_remove, f_remove}, - {"rename", 2, 2, FEARG_1, NULL, + {"rename", 2, 2, FEARG_1, arg2_string, ret_number_bool, f_rename}, {"repeat", 2, 2, FEARG_1, NULL, ret_first_arg, f_repeat}, - {"resolve", 1, 1, FEARG_1, NULL, + {"resolve", 1, 1, FEARG_1, arg1_string, ret_string, f_resolve}, {"reverse", 1, 1, FEARG_1, NULL, ret_first_arg, f_reverse}, @@ -1420,7 +1467,7 @@ static funcentry_T global_functions[] = ret_list_number, f_screenchars}, {"screencol", 0, 0, 0, NULL, ret_number, f_screencol}, - {"screenpos", 3, 3, FEARG_1, NULL, + {"screenpos", 3, 3, FEARG_1, arg3_number, ret_dict_number, f_screenpos}, {"screenrow", 0, 0, 0, NULL, ret_number, f_screenrow}, @@ -1428,7 +1475,7 @@ static funcentry_T global_functions[] = ret_string, f_screenstring}, {"search", 1, 5, FEARG_1, NULL, ret_number, f_search}, - {"searchcount", 0, 1, FEARG_1, NULL, + {"searchcount", 0, 1, FEARG_1, arg1_dict, ret_dict_any, f_searchcount}, {"searchdecl", 1, 3, FEARG_1, NULL, ret_number_bool, f_searchdecl}, @@ -1450,7 +1497,7 @@ static funcentry_T global_functions[] = ret_void, f_setcellwidths}, {"setcharpos", 2, 2, FEARG_2, NULL, ret_number_bool, f_setcharpos}, - {"setcharsearch", 1, 1, FEARG_1, NULL, + {"setcharsearch", 1, 1, FEARG_1, arg1_dict, ret_void, f_setcharsearch}, {"setcmdpos", 1, 1, FEARG_1, arg1_number, ret_number_bool, f_setcmdpos}, @@ -1458,7 +1505,7 @@ static funcentry_T global_functions[] = ret_number_bool, f_setcursorcharpos}, {"setenv", 2, 2, FEARG_2, NULL, ret_void, f_setenv}, - {"setfperm", 2, 2, FEARG_1, NULL, + {"setfperm", 2, 2, FEARG_1, arg2_string, ret_number_bool, f_setfperm}, {"setline", 2, 2, FEARG_2, NULL, ret_number_bool, f_setline}, @@ -1480,7 +1527,7 @@ static funcentry_T global_functions[] = ret_number_bool, f_settagstack}, {"setwinvar", 3, 3, FEARG_3, NULL, ret_void, f_setwinvar}, - {"sha256", 1, 1, FEARG_1, NULL, + {"sha256", 1, 1, FEARG_1, arg1_string, ret_string, #ifdef FEAT_CRYPT f_sha256 @@ -1510,7 +1557,7 @@ static funcentry_T global_functions[] = ret_number_bool, SIGN_FUNC(f_sign_unplace)}, {"sign_unplacelist", 1, 2, FEARG_1, NULL, ret_list_number, SIGN_FUNC(f_sign_unplacelist)}, - {"simplify", 1, 1, FEARG_1, NULL, + {"simplify", 1, 1, FEARG_1, arg1_string, ret_string, f_simplify}, {"sin", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_sin)}, @@ -1526,11 +1573,11 @@ static funcentry_T global_functions[] = ret_number, SOUND_FUNC(f_sound_playevent)}, {"sound_playfile", 1, 2, FEARG_1, NULL, ret_number, SOUND_FUNC(f_sound_playfile)}, - {"sound_stop", 1, 1, FEARG_1, NULL, + {"sound_stop", 1, 1, FEARG_1, arg1_number, ret_void, SOUND_FUNC(f_sound_stop)}, - {"soundfold", 1, 1, FEARG_1, NULL, + {"soundfold", 1, 1, FEARG_1, arg1_string, ret_string, f_soundfold}, - {"spellbadword", 0, 1, FEARG_1, NULL, + {"spellbadword", 0, 1, FEARG_1, arg1_string, ret_list_string, f_spellbadword}, {"spellsuggest", 1, 3, FEARG_1, NULL, ret_list_string, f_spellsuggest}, @@ -1538,9 +1585,9 @@ static funcentry_T global_functions[] = ret_list_string, f_split}, {"sqrt", 1, 1, FEARG_1, arg1_float_or_nr, ret_float, FLOAT_FUNC(f_sqrt)}, - {"srand", 0, 1, FEARG_1, NULL, + {"srand", 0, 1, FEARG_1, arg1_number, ret_list_number, f_srand}, - {"state", 0, 1, FEARG_1, NULL, + {"state", 0, 1, FEARG_1, arg1_string, ret_string, f_state}, {"str2float", 1, 1, FEARG_1, arg1_string, ret_float, FLOAT_FUNC(f_str2float)}, @@ -1570,11 +1617,11 @@ static funcentry_T global_functions[] = ret_number, f_stridx}, {"string", 1, 1, FEARG_1, NULL, ret_string, f_string}, - {"strlen", 1, 1, FEARG_1, NULL, + {"strlen", 1, 1, FEARG_1, arg1_string_or_nr, ret_number, f_strlen}, {"strpart", 2, 4, FEARG_1, NULL, ret_string, f_strpart}, - {"strptime", 2, 2, FEARG_1, NULL, + {"strptime", 2, 2, FEARG_1, arg2_string, ret_number, #ifdef HAVE_STRPTIME f_strptime @@ -1584,23 +1631,23 @@ static funcentry_T global_functions[] = }, {"strridx", 2, 3, FEARG_1, NULL, ret_number, f_strridx}, - {"strtrans", 1, 1, FEARG_1, NULL, + {"strtrans", 1, 1, FEARG_1, arg1_string, ret_string, f_strtrans}, - {"strwidth", 1, 1, FEARG_1, NULL, + {"strwidth", 1, 1, FEARG_1, arg1_string, ret_number, f_strwidth}, {"submatch", 1, 2, FEARG_1, NULL, ret_string, f_submatch}, {"substitute", 4, 4, FEARG_1, NULL, ret_string, f_substitute}, - {"swapinfo", 1, 1, FEARG_1, NULL, + {"swapinfo", 1, 1, FEARG_1, arg1_string, ret_dict_any, f_swapinfo}, - {"swapname", 1, 1, FEARG_1, NULL, + {"swapname", 1, 1, FEARG_1, arg1_string_or_nr, ret_string, f_swapname}, {"synID", 3, 3, 0, NULL, ret_number, f_synID}, {"synIDattr", 2, 3, FEARG_1, NULL, ret_string, f_synIDattr}, - {"synIDtrans", 1, 1, FEARG_1, NULL, + {"synIDtrans", 1, 1, FEARG_1, arg1_number, ret_number, f_synIDtrans}, {"synconcealed", 2, 2, 0, NULL, ret_list_any, f_synconcealed}, @@ -1610,9 +1657,9 @@ static funcentry_T global_functions[] = ret_string, f_system}, {"systemlist", 1, 2, FEARG_1, NULL, ret_list_string, f_systemlist}, - {"tabpagebuflist", 0, 1, FEARG_1, NULL, + {"tabpagebuflist", 0, 1, FEARG_1, arg1_number, ret_list_number, f_tabpagebuflist}, - {"tabpagenr", 0, 1, 0, NULL, + {"tabpagenr", 0, 1, 0, arg1_string, ret_number, f_tabpagenr}, {"tabpagewinnr", 1, 2, FEARG_1, NULL, ret_number, f_tabpagewinnr}, @@ -1746,13 +1793,13 @@ static funcentry_T global_functions[] = ret_any, f_test_unknown}, {"test_void", 0, 0, 0, NULL, ret_void, f_test_void}, - {"timer_info", 0, 1, FEARG_1, NULL, + {"timer_info", 0, 1, FEARG_1, arg1_number, ret_list_dict_any, TIMER_FUNC(f_timer_info)}, {"timer_pause", 2, 2, FEARG_1, NULL, ret_void, TIMER_FUNC(f_timer_pause)}, {"timer_start", 2, 3, FEARG_1, NULL, ret_number, TIMER_FUNC(f_timer_start)}, - {"timer_stop", 1, 1, FEARG_1, NULL, + {"timer_stop", 1, 1, FEARG_1, arg1_number, ret_void, TIMER_FUNC(f_timer_stop)}, {"timer_stopall", 0, 0, 0, NULL, ret_void, TIMER_FUNC(f_timer_stopall)}, @@ -1770,13 +1817,13 @@ static funcentry_T global_functions[] = ret_number, f_type}, {"typename", 1, 1, FEARG_1, NULL, ret_string, f_typename}, - {"undofile", 1, 1, FEARG_1, NULL, + {"undofile", 1, 1, FEARG_1, arg1_string, ret_string, f_undofile}, {"undotree", 0, 0, 0, NULL, ret_dict_any, f_undotree}, {"uniq", 1, 3, FEARG_1, NULL, ret_list_any, f_uniq}, - {"values", 1, 1, FEARG_1, NULL, + {"values", 1, 1, FEARG_1, arg1_dict, ret_list_any, f_values}, {"virtcol", 1, 1, FEARG_1, NULL, ret_number, f_virtcol}, @@ -1786,9 +1833,9 @@ static funcentry_T global_functions[] = ret_number, f_wildmenumode}, {"win_execute", 2, 3, FEARG_2, NULL, ret_string, f_win_execute}, - {"win_findbuf", 1, 1, FEARG_1, NULL, + {"win_findbuf", 1, 1, FEARG_1, arg1_number, ret_list_number, f_win_findbuf}, - {"win_getid", 0, 2, FEARG_1, NULL, + {"win_getid", 0, 2, FEARG_1, arg2_number, ret_number, f_win_getid}, {"win_gettype", 0, 1, FEARG_1, arg1_number, ret_string, f_win_gettype}, @@ -1814,11 +1861,11 @@ static funcentry_T global_functions[] = ret_list_any, f_winlayout}, {"winline", 0, 0, 0, NULL, ret_number, f_winline}, - {"winnr", 0, 1, FEARG_1, NULL, + {"winnr", 0, 1, FEARG_1, arg1_string, ret_number, f_winnr}, {"winrestcmd", 0, 0, 0, NULL, ret_string, f_winrestcmd}, - {"winrestview", 1, 1, FEARG_1, NULL, + {"winrestview", 1, 1, FEARG_1, arg1_dict, ret_void, f_winrestview}, {"winsaveview", 0, 0, 0, NULL, ret_dict_number, f_winsaveview}, @@ -2007,6 +2054,18 @@ internal_func_check_arg_types( return OK; } +/* + * Get the argument count for function "idx". + * "argcount" is the total argument count, "min_argcount" the non-optional + * argument count. + */ + void +internal_func_get_argcount(int idx, int *argcount, int *min_argcount) +{ + *argcount = global_functions[idx].f_max_argc; + *min_argcount = global_functions[idx].f_min_argc; +} + /* * Call the "f_retfunc" function to obtain the return type of function "idx". * "argtypes" is the list of argument types or NULL when there are no diff --git a/src/ex_eval.c b/src/ex_eval.c index d68e792449..dac70ab3ac 100644 --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -1670,6 +1670,8 @@ ex_catch(exarg_T *eap) for (idx = cstack->cs_idx; idx > 0; --idx) if (cstack->cs_flags[idx] & CSF_TRY) break; + if (cstack->cs_flags[idx] & CSF_TRY) + cstack->cs_flags[idx] |= CSF_CATCH; if (cstack->cs_flags[idx] & CSF_FINALLY) { // Give up for a ":catch" after ":finally" and ignore it. @@ -1963,8 +1965,8 @@ ex_endtry(exarg_T *eap) * made inactive by a ":continue", ":break", ":return", or ":finish" in * the finally clause. The latter case need not be tested since then * anything pending has already been discarded. */ - skip = did_emsg || got_int || did_throw || - !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); + skip = did_emsg || got_int || did_throw + || !(cstack->cs_flags[cstack->cs_idx] & CSF_TRUE); if (!(cstack->cs_flags[cstack->cs_idx] & CSF_TRY)) { @@ -1992,6 +1994,14 @@ ex_endtry(exarg_T *eap) { idx = cstack->cs_idx; + if (in_vim9script() + && (cstack->cs_flags[idx] & (CSF_CATCH|CSF_FINALLY)) == 0) + { + // try/endtry without any catch or finally: give an error and + // continue. + eap->errmsg = _(e_missing_catch_or_finally); + } + /* * If we stopped with the exception currently being thrown at this * try conditional since we didn't know that it doesn't have diff --git a/src/fileio.c b/src/fileio.c index 01136ca2c9..94fa0d2b93 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -5257,6 +5257,7 @@ vim_tempname( WCHAR *chartab = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; char_u *retval; char_u *p; + char_u *shname; long i; wcscpy(itmp, L""); @@ -5280,9 +5281,12 @@ vim_tempname( // Backslashes in a temp file name cause problems when filtering with // "sh". NOTE: This also checks 'shellcmdflag' to help those people who - // didn't set 'shellslash'. + // didn't set 'shellslash' but only if not using PowerShell. retval = utf16_to_enc(itmp, NULL); - if (*p_shcf == '-' || p_ssl) + shname = gettail(p_sh); + if ((*p_shcf == '-' && !(strstr((char *)shname, "powershell") != NULL + || strstr((char *)shname, "pwsh") != NULL )) + || p_ssl) for (p = retval; *p; ++p) if (*p == '\\') *p = '/'; diff --git a/src/globals.h b/src/globals.h index f4111024fb..b45de4d8c3 100644 --- a/src/globals.h +++ b/src/globals.h @@ -421,7 +421,7 @@ EXTERN type_T t_channel INIT6(VAR_CHANNEL, 0, 0, TTFLAG_STATIC, NULL, NULL); // Special value used for @#. EXTERN type_T t_number_or_string INIT6(VAR_STRING, 0, 0, TTFLAG_STATIC, NULL, NULL); -EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_unknown, NULL); +EXTERN type_T t_func_unknown INIT6(VAR_FUNC, -1, -1, TTFLAG_STATIC, &t_unknown, NULL); EXTERN type_T t_func_void INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_void, NULL); EXTERN type_T t_func_any INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_any, NULL); EXTERN type_T t_func_number INIT6(VAR_FUNC, -1, 0, TTFLAG_STATIC, &t_number, NULL); diff --git a/src/mbyte.c b/src/mbyte.c index 8ac15d38a9..b2519ecbaa 100644 --- a/src/mbyte.c +++ b/src/mbyte.c @@ -5587,7 +5587,8 @@ f_setcellwidths(typval_T *argvars, typval_T *rettv UNUSED) void f_charclass(typval_T *argvars, typval_T *rettv UNUSED) { - if (check_for_string_arg(argvars, 0) == FAIL) + if (check_for_string_arg(argvars, 0) == FAIL + || argvars[0].vval.v_string == NULL) return; rettv->vval.v_number = mb_get_class(argvars[0].vval.v_string); } diff --git a/src/misc2.c b/src/misc2.c index 164e78056f..5810e32e95 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -1396,7 +1396,9 @@ csh_like_shell(void) /* * Escape "string" for use as a shell argument with system(). * This uses single quotes, except when we know we need to use double quotes - * (MS-DOS and MS-Windows without 'shellslash' set). + * (MS-DOS and MS-Windows not using PowerShell and without 'shellslash' set). + * PowerShell also uses a novel escaping for enclosed single quotes - double + * them up. * Escape a newline, depending on the 'shell' option. * When "do_special" is TRUE also replace "!", "%", "#" and things starting * with "<" like "". @@ -1412,6 +1414,11 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline) char_u *escaped_string; int l; int csh_like; + char_u *shname; + int powershell; +# ifdef MSWIN + int double_quotes; +# endif // Only csh and similar shells expand '!' within single quotes. For sh and // the like we must not put a backslash before it, it will be taken @@ -1419,12 +1426,21 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline) // Csh also needs to have "\n" escaped twice when do_special is set. csh_like = csh_like_shell(); + // PowerShell uses it's own version for quoting single quotes + shname = gettail(p_sh); + powershell = strstr((char *)shname, "pwsh") != NULL; +# ifdef MSWIN + powershell = powershell || strstr((char *)shname, "powershell") != NULL; + // PowerShell only accepts single quotes so override shellslash. + double_quotes = !powershell && !p_ssl; +# endif + // First count the number of extra bytes required. length = (unsigned)STRLEN(string) + 3; // two quotes and a trailing NUL for (p = string; *p != NUL; MB_PTR_ADV(p)) { # ifdef MSWIN - if (!p_ssl) + if (double_quotes) { if (*p == '"') ++length; // " -> "" @@ -1432,7 +1448,12 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline) else # endif if (*p == '\'') - length += 3; // ' => '\'' + { + if (powershell) + length +=2; // ' => '' + else + length += 3; // ' => '\'' + } if ((*p == '\n' && (csh_like || do_newline)) || (*p == '!' && (csh_like || do_special))) { @@ -1455,7 +1476,7 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline) // add opening quote # ifdef MSWIN - if (!p_ssl) + if (double_quotes) *d++ = '"'; else # endif @@ -1464,7 +1485,7 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline) for (p = string; *p != NUL; ) { # ifdef MSWIN - if (!p_ssl) + if (double_quotes) { if (*p == '"') { @@ -1478,10 +1499,18 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline) # endif if (*p == '\'') { - *d++ = '\''; - *d++ = '\\'; - *d++ = '\''; - *d++ = '\''; + if (powershell) + { + *d++ = '\''; + *d++ = '\''; + } + else + { + *d++ = '\''; + *d++ = '\\'; + *d++ = '\''; + *d++ = '\''; + } ++p; continue; } @@ -1507,7 +1536,7 @@ vim_strsave_shellescape(char_u *string, int do_special, int do_newline) // add terminating quote and finish with a NUL # ifdef MSWIN - if (!p_ssl) + if (double_quotes) *d++ = '"'; else # endif diff --git a/src/normal.c b/src/normal.c index 7b08ab64c4..85bc28e33f 100644 --- a/src/normal.c +++ b/src/normal.c @@ -5759,8 +5759,8 @@ may_start_select(int c) n_start_visual_mode(int c) { #ifdef FEAT_CONCEAL - // Check for redraw before changing the state. - conceal_check_cursor_line(); + int cursor_line_was_concealed = curwin->w_p_cole > 0 + && conceal_cursor_line(curwin); #endif VIsual_mode = c; @@ -5782,8 +5782,8 @@ n_start_visual_mode(int c) setmouse(); #ifdef FEAT_CONCEAL - // Check for redraw after changing the state. - conceal_check_cursor_line(); + // Check if redraw is needed after changing the state. + conceal_check_cursor_line(cursor_line_was_concealed); #endif if (p_smd && msg_silent == 0) diff --git a/src/ops.c b/src/ops.c index 8f2ca8e369..59d5bff50d 100644 --- a/src/ops.c +++ b/src/ops.c @@ -545,6 +545,8 @@ block_insert( spaces -= off; count -= off; } + if (spaces < 0) // can happen when the cursor was moved + spaces = 0; newp = alloc(STRLEN(oldp) + s_len + count + 1); if (newp == NULL) @@ -1455,6 +1457,9 @@ op_insert(oparg_T *oap, long count1) struct block_def bd; int i; pos_T t1; + pos_T start_insert; + // offset when cursor was moved in insert mode + int offset = 0; // edit() changes this - record it for OP_APPEND bd.is_MAX = (curwin->w_curswant == MAXCOL); @@ -1526,6 +1531,7 @@ op_insert(oparg_T *oap, long count1) } t1 = oap->start; + start_insert = curwin->w_cursor; (void)edit(NUL, FALSE, (linenr_T)count1); // When a tab was inserted, and the characters in front of the tab @@ -1564,30 +1570,38 @@ op_insert(oparg_T *oap, long count1) if (oap->start.lnum == curbuf->b_op_start_orig.lnum && !bd.is_MAX && !did_indent) { - if (oap->op_type == OP_INSERT - && oap->start.col + oap->start.coladd - != curbuf->b_op_start_orig.col - + curbuf->b_op_start_orig.coladd) + int t = getviscol2(curbuf->b_op_start_orig.col, + curbuf->b_op_start_orig.coladd); + + if (!bd.is_MAX) { - int t = getviscol2(curbuf->b_op_start_orig.col, - curbuf->b_op_start_orig.coladd); - oap->start.col = curbuf->b_op_start_orig.col; - pre_textlen -= t - oap->start_vcol; - oap->start_vcol = t; + if (oap->op_type == OP_INSERT + && oap->start.col + oap->start.coladd + != curbuf->b_op_start_orig.col + + curbuf->b_op_start_orig.coladd) + { + oap->start.col = curbuf->b_op_start_orig.col; + pre_textlen -= t - oap->start_vcol; + oap->start_vcol = t; + } + else if (oap->op_type == OP_APPEND + && oap->end.col + oap->end.coladd + >= curbuf->b_op_start_orig.col + + curbuf->b_op_start_orig.coladd) + { + oap->start.col = curbuf->b_op_start_orig.col; + // reset pre_textlen to the value of OP_INSERT + pre_textlen += bd.textlen; + pre_textlen -= t - oap->start_vcol; + oap->start_vcol = t; + oap->op_type = OP_INSERT; + } } - else if (oap->op_type == OP_APPEND - && oap->end.col + oap->end.coladd - >= curbuf->b_op_start_orig.col - + curbuf->b_op_start_orig.coladd) + else if (bd.is_MAX && oap->op_type == OP_APPEND) { - int t = getviscol2(curbuf->b_op_start_orig.col, - curbuf->b_op_start_orig.coladd); - oap->start.col = curbuf->b_op_start_orig.col; // reset pre_textlen to the value of OP_INSERT pre_textlen += bd.textlen; pre_textlen -= t - oap->start_vcol; - oap->start_vcol = t; - oap->op_type = OP_INSERT; } } @@ -1617,13 +1631,28 @@ op_insert(oparg_T *oap, long count1) len = STRLEN(firstline); add = bd.textcol; if (oap->op_type == OP_APPEND) + { add += bd.textlen; + // account for pressing cursor in insert mode when '$' was used + if (bd.is_MAX + && (start_insert.lnum == Insstart.lnum + && start_insert.col > Insstart.col)) + { + offset = (start_insert.col - Insstart.col); + add -= offset; + if (oap->end_vcol > offset) + oap->end_vcol -= (offset + 1); + else + // moved outside of the visual block, what to do? + return; + } + } if ((size_t)add > len) firstline += len; // short line, point to the NUL else firstline += add; - if (pre_textlen >= 0 - && (ins_len = (long)STRLEN(firstline) - pre_textlen) > 0) + if (pre_textlen >= 0 && (ins_len = + (long)STRLEN(firstline) - pre_textlen - offset) > 0) { ins_text = vim_strnsave(firstline, ins_len); if (ins_text != NULL) diff --git a/src/option.c b/src/option.c index f14c8fb747..b959c7c04c 100644 --- a/src/option.c +++ b/src/option.c @@ -950,6 +950,27 @@ set_init_3(void) options[idx_srr].def_val[VI_DEFAULT] = p_srr; } } +# ifdef MSWIN + // Windows PowerShell output is UTF-16 with BOM so re-encode to the + // current codepage. + else if ( fnamecmp(p, "powershell") == 0 + || fnamecmp(p, "powershell.exe") == 0 + ) + { +# if defined(FEAT_QUICKFIX) + if (do_sp) + { + p_sp = (char_u *)"2>&1 | Out-File -Encoding default"; + options[idx_sp].def_val[VI_DEFAULT] = p_sp; + } +# endif + if (do_srr) + { + p_srr = (char_u *)"2>&1 | Out-File -Encoding default"; + options[idx_srr].def_val[VI_DEFAULT] = p_srr; + } + } +#endif else // Always use POSIX shell style redirection if we reach this if ( fnamecmp(p, "sh") == 0 @@ -962,6 +983,7 @@ set_init_3(void) || fnamecmp(p, "fish") == 0 || fnamecmp(p, "ash") == 0 || fnamecmp(p, "dash") == 0 + || fnamecmp(p, "pwsh") == 0 # ifdef MSWIN || fnamecmp(p, "cmd") == 0 || fnamecmp(p, "sh.exe") == 0 @@ -973,6 +995,7 @@ set_init_3(void) || fnamecmp(p, "bash.exe") == 0 || fnamecmp(p, "cmd.exe") == 0 || fnamecmp(p, "dash.exe") == 0 + || fnamecmp(p, "pwsh.exe") == 0 # endif ) { @@ -982,7 +1005,10 @@ set_init_3(void) # ifdef MSWIN p_sp = (char_u *)">%s 2>&1"; # else - p_sp = (char_u *)"2>&1| tee"; + if (fnamecmp(p, "pwsh") == 0) + p_sp = (char_u *)">%s 2>&1"; + else + p_sp = (char_u *)"2>&1| tee"; # endif options[idx_sp].def_val[VI_DEFAULT] = p_sp; } @@ -1002,11 +1028,36 @@ set_init_3(void) * Set 'shellcmdflag', 'shellxquote', and 'shellquote' depending on the * 'shell' option. * This is done after other initializations, where 'shell' might have been - * set, but only if they have not been set before. Default for p_shcf is - * "/c", for p_shq is "". For "sh" like shells it is changed here to - * "-c" and "\"". And for Win32 we need to set p_sxq instead. + * set, but only if they have not been set before. + * Default values depend on shell (cmd.exe is default shell): + * + * p_shcf p_sxq + * cmd.exe - "/c" "(" + * powershell.exe - "-Command" "\"" + * pwsh.exe - "-c" "\"" + * "sh" like shells - "-c" "\"" + * + * For Win32 p_sxq is set instead of p_shq to include shell redirection. */ - if (strstr((char *)gettail(p_sh), "sh") != NULL) + if (strstr((char *)gettail(p_sh), "powershell") != NULL) + { + int idx_opt; + + idx_opt = findoption((char_u *)"shcf"); + if (idx_opt >= 0 && !(options[idx_opt].flags & P_WAS_SET)) + { + p_shcf = (char_u*)"-Command"; + options[idx_opt].def_val[VI_DEFAULT] = p_shcf; + } + + idx_opt = findoption((char_u *)"sxq"); + if (idx_opt >= 0 && !(options[idx_opt].flags & P_WAS_SET)) + { + p_sxq = (char_u*)"\""; + options[idx_opt].def_val[VI_DEFAULT] = p_sxq; + } + } + else if (strstr((char *)gettail(p_sh), "sh") != NULL) { int idx3; diff --git a/src/os_win32.c b/src/os_win32.c index a966c53495..eff2269f8c 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -2135,6 +2135,7 @@ executable_exists(char *name, char_u **path, int use_path, int use_pathext) char_u *pathbuf = NULL; char_u *pathext = NULL; char_u *pathextbuf = NULL; + char_u *shname = NULL; int noext = FALSE; int retval = FALSE; @@ -2142,7 +2143,10 @@ executable_exists(char *name, char_u **path, int use_path, int use_pathext) return FALSE; // Using the name directly when a Unix-shell like 'shell'. - if (strstr((char *)gettail(p_sh), "sh") != NULL) + shname = gettail(p_sh); + if (strstr((char *)shname, "sh") != NULL && + !(strstr((char *)shname, "powershell") != NULL + || strstr((char *)shname, "pwsh") != NULL)) noext = TRUE; if (use_pathext) diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro index c1ac55f5b9..3faff292a7 100644 --- a/src/proto/evalfunc.pro +++ b/src/proto/evalfunc.pro @@ -6,6 +6,7 @@ int find_internal_func(char_u *name); int has_internal_func(char_u *name); char *internal_func_name(int idx); int internal_func_check_arg_types(type_T **types, int idx, int argcount, cctx_T *cctx); +void internal_func_get_argcount(int idx, int *argcount, int *min_argcount); type_T *internal_func_ret_type(int idx, int argcount, type_T **argtypes); int internal_func_is_map(int idx); int check_internal_func(int idx, int argcount); @@ -21,7 +22,6 @@ void f_has(typval_T *argvars, typval_T *rettv); int dynamic_feature(char_u *feature); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); void range_list_materialize(list_T *list); -float_T vim_round(float_T f); long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, typval_T *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); void f_string(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/proto/float.pro b/src/proto/float.pro index e14ce24735..806eba1f61 100644 --- a/src/proto/float.pro +++ b/src/proto/float.pro @@ -1,4 +1,4 @@ -/* math.c */ +/* float.c */ int string2float(char_u *text, float_T *value); void f_abs(typval_T *argvars, typval_T *rettv); void f_acos(typval_T *argvars, typval_T *rettv); @@ -17,6 +17,7 @@ void f_isnan(typval_T *argvars, typval_T *rettv); void f_log(typval_T *argvars, typval_T *rettv); void f_log10(typval_T *argvars, typval_T *rettv); void f_pow(typval_T *argvars, typval_T *rettv); +float_T vim_round(float_T f); void f_round(typval_T *argvars, typval_T *rettv); void f_sin(typval_T *argvars, typval_T *rettv); void f_sinh(typval_T *argvars, typval_T *rettv); diff --git a/src/proto/screen.pro b/src/proto/screen.pro index fc45fa15fe..6d1993d2b5 100644 --- a/src/proto/screen.pro +++ b/src/proto/screen.pro @@ -1,6 +1,6 @@ /* screen.c */ int conceal_cursor_line(win_T *wp); -void conceal_check_cursor_line(void); +void conceal_check_cursor_line(int was_concealed); int get_wcr_attr(win_T *wp); void win_draw_end(win_T *wp, int c1, int c2, int draw_margin, int row, int endrow, hlf_T hl); int compute_foldcolumn(win_T *wp, int col); diff --git a/src/register.c b/src/register.c index 4774e2a995..93ee7aad97 100644 --- a/src/register.c +++ b/src/register.c @@ -1455,6 +1455,8 @@ yank_copy_line(struct block_def *bd, long y_idx, int exclude_trailing_space) { char_u *pnew; + if (exclude_trailing_space) + bd->endspaces = 0; if ((pnew = alloc(bd->startspaces + bd->endspaces + bd->textlen + 1)) == NULL) return FAIL; @@ -2747,7 +2749,7 @@ write_reg_contents_lst( &yank_type) == FAIL) return; - str_to_reg(y_current, yank_type, (char_u *) strings, -1, block_len, TRUE); + str_to_reg(y_current, yank_type, (char_u *)strings, -1, block_len, TRUE); finish_write_reg(name, old_y_previous, old_y_current); } diff --git a/src/screen.c b/src/screen.c index b555d53921..5143a88c64 100644 --- a/src/screen.c +++ b/src/screen.c @@ -83,16 +83,26 @@ conceal_cursor_line(win_T *wp) /* * Check if the cursor line needs to be redrawn because of 'concealcursor'. + * To be called after changing the state, "was_concealed" is the value of + * "conceal_cursor_line()" before the change. + * " */ void -conceal_check_cursor_line(void) +conceal_check_cursor_line(int was_concealed) { - if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin)) + if (curwin->w_p_cole > 0 && conceal_cursor_line(curwin) != was_concealed) { + int wcol = curwin->w_wcol; + need_cursor_line_redraw = TRUE; // Need to recompute cursor column, e.g., when starting Visual mode // without concealing. curs_columns(TRUE); + + // When concealing now w_wcol will be computed wrong, keep the previous + // value, it will be updated in win_line(). + if (!was_concealed) + curwin->w_wcol = wcol; } } #endif diff --git a/src/structs.h b/src/structs.h index 7e521a1013..e30c59bd4f 100644 --- a/src/structs.h +++ b/src/structs.h @@ -936,13 +936,14 @@ typedef struct { # define CSF_TRY 0x0100 // is a ":try" # define CSF_FINALLY 0x0200 // ":finally" has been passed -# define CSF_THROWN 0x0400 // exception thrown to this try conditional -# define CSF_CAUGHT 0x0800 // exception caught by this try conditional -# define CSF_SILENT 0x1000 // "emsg_silent" reset by ":try" +# define CSF_CATCH 0x0400 // ":catch" has been seen +# define CSF_THROWN 0x0800 // exception thrown to this try conditional +# define CSF_CAUGHT 0x1000 // exception caught by this try conditional +# define CSF_SILENT 0x2000 // "emsg_silent" reset by ":try" // Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset // (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. // -#define CSF_FUNC_DEF 0x2000 // a function was defined in this block +#define CSF_FUNC_DEF 0x4000 // a function was defined in this block /* * What's pending for being reactivated at the ":endtry" of this try @@ -1610,6 +1611,8 @@ typedef struct 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 + int uf_args_visible; // normally uf_args.ga_len, less when + // compiling default argument expression. // for :def (for :function uf_ret_type is NULL) type_T **uf_arg_types; // argument types (count == uf_args.ga_len) diff --git a/src/testdir/check.vim b/src/testdir/check.vim index 216b37bc52..b8a2125e68 100644 --- a/src/testdir/check.vim +++ b/src/testdir/check.vim @@ -14,6 +14,17 @@ func CheckFeature(name) endif endfunc +" Command to check for the absence of a feature. +command -nargs=1 CheckNotFeature call CheckNotFeature() +func CheckNotFeature(name) + if !has(a:name, 1) + throw 'Checking for non-existent feature ' .. a:name + endif + if has(a:name) + throw 'Skipped: ' .. a:name .. ' feature present' + endif +endfunc + " Command to check for the presence of a working option. command -nargs=1 CheckOption call CheckOption() func CheckOption(name) diff --git a/src/testdir/dumps/Test_popupwin_atcursor_pos.dump b/src/testdir/dumps/Test_popupwin_atcursor_pos.dump index a42b6e6ff5..3a4f0c2c7f 100644 --- a/src/testdir/dumps/Test_popupwin_atcursor_pos.dump +++ b/src/testdir/dumps/Test_popupwin_atcursor_pos.dump @@ -1,12 +1,12 @@ |-+0&#ffffff0@59| @14 |-@59| @14 -|-@25|%|-@16>@|-@14| @14 +|-@25|%|-@16|@|-@14| @14 |-@25|f+0#0000001#ffd7ff255|i|R|S|t| |-+0#0000000#ffffff0@6|F+0#0000001#ffd7ff255|i|r|s|t| |-+0#0000000#ffffff0@14| @14 |-@25|s+0#0000001#ffd7ff255|e|C|O|n|d|-+0#0000000#ffffff0@6|S+0#0000001#ffd7ff255|e|c|o|n|D|-+0#0000000#ffffff0@14| @14 |-@59| @14 |-@1|f+0#0000001#ffd7ff255|i|r|s|t| |-+0#0000000#ffffff0@6|F+0#0000001#ffd7ff255|I|r|s|T| |-+0#0000000#ffffff0@38| @14 -|-@1|s+0#0000001#ffd7ff255|e|c|o|n|d|-+0#0000000#ffffff0@6|S+0#0000001#ffd7ff255|E|c|o|N|D|-+0#0000000#ffffff0@38| @14 -|-@1|#|-@16|&|-@38| @14 +|-@1|s+0#0000001#ffd7ff255|e|c|o|n|d|-+0#0000000#ffffff0@6|S+0#0000001#ffd7ff255|E|c|o|N|D|-+0#0000000#ffffff0@6|m+0#0000001#ffd7ff255|a|r|k|-+0#0000000#ffffff0@27| @14 +|-@1|#|-@16|&|-@4| @1>X|-@21| @23 |-@59| @14 |-@59| @14 -@57|3|,|4|5| @9|T|o|p| +@57|9|,|3|8| @9|T|o|p| diff --git a/src/testdir/dumps/Test_visual_block_with_virtualedit.dump b/src/testdir/dumps/Test_visual_block_with_virtualedit.dump new file mode 100644 index 0000000000..2991a633b6 --- /dev/null +++ b/src/testdir/dumps/Test_visual_block_with_virtualedit.dump @@ -0,0 +1,8 @@ +|a+0&#e0e0e08@5> +0&#ffffff0@43 +|b+0&#e0e0e08@3| @2| +0&#ffffff0@42 +|c+0&#e0e0e08@1| @4| +0&#ffffff0@42 +|~+0#4040ff13&| @48 +|~| @48 +|~| @48 +|~| @48 +|-+2#0000000&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@3|3|x|7| @6|1|,|7| @10|A|l@1| diff --git a/src/testdir/dumps/Test_visual_block_with_virtualedit2.dump b/src/testdir/dumps/Test_visual_block_with_virtualedit2.dump new file mode 100644 index 0000000000..3c62156128 --- /dev/null +++ b/src/testdir/dumps/Test_visual_block_with_virtualedit2.dump @@ -0,0 +1,8 @@ +|a+0&#e0e0e08@5| | +0&#ffffff0@42 +|b+0&#e0e0e08@3| @2| +0&#ffffff0@42 +|c+0&#e0e0e08@1> +0&#ffffff0| +0&#e0e0e08@3| +0&#ffffff0@42 +|~+0#4040ff13&| @48 +|~| @48 +|~| @48 +|~| @48 +|-+2#0000000&@1| |V|I|S|U|A|L| |B|L|O|C|K| |-@1| +0&&@3|3|x|3| @6|3|,|3| @10|A|l@1| diff --git a/src/testdir/test_blockedit.vim b/src/testdir/test_blockedit.vim index de56f23cbb..29e24759fa 100644 --- a/src/testdir/test_blockedit.vim +++ b/src/testdir/test_blockedit.vim @@ -28,4 +28,52 @@ func Test_blockinsert_delete() bwipe! endfunc +func Test_blockappend_eol_cursor() + new + " Test 1 Move 1 char left + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "norm! gg$\2jA\x\" + call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) + " Test 2 Move 2 chars left + sil %d + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "norm! gg$\2jA\\x\" + call assert_equal(['axaa', 'bxbb', 'cxcc'], getline(1, '$')) + " Test 3 Move 3 chars left (outside of the visual selection) + sil %d + call setline(1, ['aaa', 'bbb', 'ccc']) + exe "norm! ggl$\2jA\\\x\" + call assert_equal(['xaaa', 'bbb', 'ccc'], getline(1, '$')) + bw! +endfunc + +func Test_blockappend_eol_cursor2() + new + " Test 1 Move 1 char left + call setline(1, ['aaaaa', 'bbb', 'ccccc']) + exe "norm! gg\$2jA\x\" + call assert_equal(['aaaaxa', 'bbbx', 'ccccxc'], getline(1, '$')) + " Test 2 Move 2 chars left + sil %d + call setline(1, ['aaaaa', 'bbb', 'ccccc']) + exe "norm! gg\$2jA\\x\" + call assert_equal(['aaaxaa', 'bbbx', 'cccxcc'], getline(1, '$')) + " Test 3 Move 3 chars left (to the beginning of the visual selection) + sil %d + call setline(1, ['aaaaa', 'bbb', 'ccccc']) + exe "norm! gg\$2jA\\\x\" + call assert_equal(['aaxaaa', 'bbxb', 'ccxccc'], getline(1, '$')) + " Test 4 Move 3 chars left (outside of the visual selection) + sil %d + call setline(1, ['aaaaa', 'bbb', 'ccccc']) + exe "norm! ggl\$2jA\\\x\" + call assert_equal(['aaxaaa', 'bbxb', 'ccxccc'], getline(1, '$')) + " Test 5 Move 4 chars left (outside of the visual selection) + sil %d + call setline(1, ['aaaaa', 'bbb', 'ccccc']) + exe "norm! ggl\$2jA\\\\x\" + call assert_equal(['axaaaa', 'bxbb', 'cxcccc'], getline(1, '$')) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py index e6e6808e75..27a7c77de0 100644 --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -110,6 +110,11 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler): print("sending: {0}".format(cmd)) self.request.sendall(cmd.encode('utf-8')) response = "ok" + elif decoded[1] == 'echoerr': + cmd = '["ex","echoerr \\\"this is an error\\\""]' + print("sending: {0}".format(cmd)) + self.request.sendall(cmd.encode('utf-8')) + response = "ok" elif decoded[1] == 'bad command': cmd = '["ex","foo bar"]' print("sending: {0}".format(cmd)) diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index e9cc258f04..37dfd25861 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -115,6 +115,18 @@ func Ch_communicate(port) call WaitForAssert({-> assert_equal("added2", getline("$"))}) call assert_equal('added1', getline(line('$') - 1)) + " Request command "echoerr 'this is an error'". + " This will throw an exception, catch it here. + let caught = 'no' + try + call assert_equal('ok', ch_evalexpr(handle, 'echoerr')) + catch /this is an error/ + let caught = 'yes' + endtry + if caught != 'yes' + call assert_report("Expected exception from error message") + endif + " Request command "foo bar", which fails silently. call assert_equal('ok', ch_evalexpr(handle, 'bad command')) call WaitForAssert({-> assert_match("E492:.*foo bar", v:errmsg)}) diff --git a/src/testdir/test_debugger.vim b/src/testdir/test_debugger.vim index dbf2000462..97e115bf3e 100644 --- a/src/testdir/test_debugger.vim +++ b/src/testdir/test_debugger.vim @@ -975,8 +975,7 @@ func Test_debug_def_and_legacy_function() call RunDbgCmd(buf, 'cont') call StopVimInTerminal(buf) - call delete('Xtest1.vim') - call delete('Xtest2.vim') + call delete('XtestDebug.vim') endfunc func Test_debug_def_function() @@ -1017,6 +1016,13 @@ func Test_debug_def_function() # comment echo "second" enddef + def g:FuncForLoop() + eval 1 + for i in [11, 22, 33] + eval i + endfor + echo "done" + enddef END call writefile(file, 'Xtest.vim') @@ -1062,6 +1068,15 @@ func Test_debug_def_function() call RunDbgCmd(buf, ':call FuncComment()', ['function FuncComment', 'line 2: echo "first" .. "one"']) call RunDbgCmd(buf, ':breakadd func 3 FuncComment') call RunDbgCmd(buf, 'cont', ['function FuncComment', 'line 5: echo "second"']) + call RunDbgCmd(buf, 'cont') + + call RunDbgCmd(buf, ':breakadd func 2 FuncForLoop') + call RunDbgCmd(buf, ':call FuncForLoop()', ['function FuncForLoop', 'line 2: for i in [11, 22, 33]']) + call RunDbgCmd(buf, 'echo i', ['11']) + call RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 3: eval i']) + call RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 4: endfor']) + call RunDbgCmd(buf, 'next', ['function FuncForLoop', 'line 2: for i in [11, 22, 33]']) + call RunDbgCmd(buf, 'echo i', ['22']) call RunDbgCmd(buf, 'cont') call StopVimInTerminal(buf) diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 50293e524d..e78a1f4ec0 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -192,6 +192,7 @@ let s:filename_checks = { \ 'gdb': ['.gdbinit'], \ 'gdmo': ['file.mo', 'file.gdmo'], \ 'gedcom': ['file.ged', 'lltxxxxx.txt', '/tmp/lltmp', '/tmp/lltmp-file', 'any/tmp/lltmp', 'any/tmp/lltmp-file'], + \ 'gemtext': ['file.gmi', 'file.gemini'], \ 'gift': ['file.gift'], \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG'], \ 'gitconfig': ['file.git/config', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'], @@ -260,6 +261,7 @@ let s:filename_checks = { \ 'jovial': ['file.jov', 'file.j73', 'file.jovial'], \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'], \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb'], + \ 'jsonc': ['file.jsonc'], \ 'jsp': ['file.jsp'], \ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file'], \ 'kivy': ['file.kv'], diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index 23af53274d..a4ca5d45ff 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -169,7 +169,8 @@ func Test_strwidth() if has('float') call assert_equal(3, strwidth(1.2)) - call CheckDefExecAndScriptFailure(['echo strwidth(1.2)'], 'E806:') + call CheckDefFailure(['echo strwidth(1.2)'], 'E1013:') + call CheckScriptFailure(['vim9script', 'echo strwidth(1.2)'], 'E806:') endif set ambiwidth& @@ -236,7 +237,7 @@ func Test_str2nr() call assert_fails('call str2nr({->2})', 'E729:') if has('float') call assert_equal(1, str2nr(1.2)) - call CheckDefExecFailure(['echo str2nr(1.2)'], 'E1013:') + call CheckDefFailure(['echo str2nr(1.2)'], 'E1013:') call CheckScriptFailure(['vim9script', 'echo str2nr(1.2)'], 'E806:') endif call assert_fails('call str2nr(10, [])', 'E745:') @@ -499,7 +500,8 @@ func Test_simplify() call assert_fails('call simplify({})', 'E731:') if has('float') call assert_equal('1.2', simplify(1.2)) - call CheckDefExecAndScriptFailure(['echo simplify(1.2)'], 'E806:') + call CheckDefFailure(['echo simplify(1.2)'], 'E1013:') + call CheckScriptFailure(['vim9script', 'echo simplify(1.2)'], 'E806:') endif endfunc @@ -2169,6 +2171,8 @@ func Test_charclass() call assert_equal(1, charclass('.')) call assert_equal(2, charclass('x')) call assert_equal(3, charclass("\u203c")) + " this used to crash vim + call assert_equal(0, "xxx"[-1]->charclass()) endfunc func Test_eventhandler() diff --git a/src/testdir/test_glob2regpat.vim b/src/testdir/test_glob2regpat.vim index ab459bba33..18e22dc662 100644 --- a/src/testdir/test_glob2regpat.vim +++ b/src/testdir/test_glob2regpat.vim @@ -5,7 +5,8 @@ source vim9.vim func Test_glob2regpat_invalid() if has('float') call assert_equal('^1\.33$', glob2regpat(1.33)) - call CheckDefExecAndScriptFailure(['echo glob2regpat(1.33)'], 'E806:') + call CheckDefFailure(['echo glob2regpat(1.2)'], 'E1013:') + call CheckScriptFailure(['vim9script', 'echo glob2regpat(1.2)'], 'E806:') endif call assert_fails('call glob2regpat("}")', 'E219:') call assert_fails('call glob2regpat("{")', 'E220:') diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim index 3da93925f9..ef8b9d680a 100644 --- a/src/testdir/test_gui.vim +++ b/src/testdir/test_gui.vim @@ -1123,6 +1123,8 @@ func TestGuiTabToolTip() endfunc func Test_gui_tablabel_tooltip() + CheckNotFeature gui_athena + %bw! " Removing the tabline at the end of this test, reduces the window height by " one. Save and restore it after the test. diff --git a/src/testdir/test_lambda.vim b/src/testdir/test_lambda.vim index 60d58c542b..e173ea3893 100644 --- a/src/testdir/test_lambda.vim +++ b/src/testdir/test_lambda.vim @@ -330,6 +330,7 @@ func Test_closure_error() let caught_932 = 1 endtry call assert_equal(1, caught_932) + call delete('Xscript') endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 68d7db87ea..62e19fa5d2 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -1437,6 +1437,7 @@ endfunc func Test_popup_atcursor_pos() CheckScreendump + CheckFeature conceal let lines =<< trim END call setline(1, repeat([repeat('-', 60)], 15)) @@ -1462,6 +1463,13 @@ func Test_popup_atcursor_pos() \ moved: range(3), \ mousemoved: range(3), \ }) + + normal 9G27|Rconcealed X + syn match Hidden /concealed/ conceal + set conceallevel=2 concealcursor=n + redraw + normal 0fX + call popup_atcursor('mark', {}) END call writefile(lines, 'XtestPopupAtcursorPos') let buf = RunVimInTerminal('-S XtestPopupAtcursorPos', #{rows: 12}) @@ -1542,24 +1550,32 @@ func Test_popup_filter() redraw " e is consumed by the filter + let g:eaten = '' call feedkeys('e', 'xt') call assert_equal('e', g:eaten) call feedkeys("\", 'xt') call assert_equal("\", g:eaten) " 0 is ignored by the filter + let g:ignored = '' normal $ call assert_equal(9, getcurpos()[2]) call feedkeys('0', 'xt') call assert_equal('0', g:ignored) - normal! l - call assert_equal(2, getcurpos()[2]) + + if has('win32') && has('gui_running') + echo "FIXME: this check is very flaky on MS-Windows GUI, the cursor doesn't move" + else + call assert_equal(1, getcurpos()[2]) + endif " x closes the popup call feedkeys('x', 'xt') call assert_equal("\", g:eaten) call assert_equal(-1, winbufnr(winid)) + unlet g:eaten + unlet g:ignored delfunc MyPopupFilter call popup_clear() endfunc diff --git a/src/testdir/test_recover.vim b/src/testdir/test_recover.vim index 2db662f017..6ef4376def 100644 --- a/src/testdir/test_recover.vim +++ b/src/testdir/test_recover.vim @@ -202,10 +202,16 @@ func Test_recover_corrupted_swap_file() " Not all fields are written in a system-independent manner. Detect whether " the test is running on a little or big-endian system, so the correct " corruption values can be set. - let little_endian = b[1008:1015] == 0z33323130.00000000 + let little_endian = b[1008:1011] == 0z33323130 + " The swap file header fields can be either 32-bit or 64-bit. + let system_64bit = b[1012:1015] == 0z00000000 " clear the B0_MAGIC_LONG field - let b[1008:1015] = 0z0000000000000000 + if system_64bit + let b[1008:1015] = 0z00000000.00000000 + else + let b[1008:1011] = 0z00000000 + endif call writefile(b, sn) let msg = execute('recover Xfile1') call assert_match('the file has been damaged', msg) @@ -243,7 +249,11 @@ func Test_recover_corrupted_swap_file() " set the block number in a pointer entry to a negative number let b = copy(save_b) - let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000 + if system_64bit + let b[4104:4111] = little_endian ? 0z00000000.00000080 : 0z80000000.00000000 + else + let b[4104:4107] = little_endian ? 0z00000080 : 0z80000000 + endif call writefile(b, sn) call assert_fails('recover Xfile1', 'E312:') call assert_equal('Xfile1', @%) @@ -261,7 +271,11 @@ func Test_recover_corrupted_swap_file() " set the number of lines in the data block to zero let b = copy(save_b) - let b[8208:8215] = 0z00000000.00000000 + if system_64bit + let b[8208:8215] = 0z00000000.00000000 + else + let b[8208:8211] = 0z00000000 + endif call writefile(b, sn) call assert_fails('recover Xfile1', 'E312:') call assert_equal('Xfile1', @%) @@ -271,7 +285,11 @@ func Test_recover_corrupted_swap_file() " use an invalid text start for the lines in a data block let b = copy(save_b) - let b[8216:8219] = 0z00000000 + if system_64bit + let b[8216:8219] = 0z00000000 + else + let b[8212:8215] = 0z00000000 + endif call writefile(b, sn) call assert_fails('recover Xfile1', 'E312:') call assert_equal('Xfile1', @%) diff --git a/src/testdir/test_shell.vim b/src/testdir/test_shell.vim index f992b8ede6..f5a3e98997 100644 --- a/src/testdir/test_shell.vim +++ b/src/testdir/test_shell.vim @@ -19,13 +19,18 @@ func Test_shell_options() \ ['ash', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], \ ['dash', '-c', '2>&1| tee', '', '>%s 2>&1', '', ''], \ ['csh', '-c', '|& tee', '', '>&', '', ''], - \ ['tcsh', '-c', '|& tee', '', '>&', '', '']] + \ ['tcsh', '-c', '|& tee', '', '>&', '', ''], + \ ['pwsh', '-c', '>%s 2>&1', '', '>%s 2>&1', '', '']] endif if has('win32') let shells += [['cmd', '/c', '>%s 2>&1', '', '>%s 2>&1', '"&|<>()@^', ''], \ ['cmd.exe', '/c', '>%s 2>&1', '', '>%s 2>&1', '"&|<>()@^', '('], - \ ['powershell.exe', '-c', '>', '', '>', '"&|<>()@^', '"'], - \ ['powershell', '-c', '>', '', '>', '"&|<>()@^', '"'], + \ ['powershell.exe', '-Command', '2>&1 | Out-File -Encoding default', + \ '', '2>&1 | Out-File -Encoding default', '"&|<>()@^', '"'], + \ ['powershell', '-Command', '2>&1 | Out-File -Encoding default', '', + \ '2>&1 | Out-File -Encoding default', '"&|<>()@^', '"'], + \ ['pwsh.exe', '-c', '>%s 2>&1', '', '>%s 2>&1', '"&|<>()@^', '"'], + \ ['pwsh', '-c', '>%s 2>&1', '', '>%s 2>&1', '"&|<>()@^', '"'], \ ['sh.exe', '-c', '>%s 2>&1', '', '>%s 2>&1', '"&|<>()@^', '"'], \ ['ksh.exe', '-c', '>%s 2>&1', '', '>%s 2>&1', '"&|<>()@^', '"'], \ ['mksh.exe', '-c', '>%s 2>&1', '', '>%s 2>&1', '"&|<>()@^', '"'], @@ -58,6 +63,10 @@ func Test_shell_options() if e[0] =~# '.*csh$' || e[0] =~# '.*csh.exe$' let str1 = "'cmd \"arg1\" '\\''arg2'\\'' \\!%#'" let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\\\!\\%\\#'" + elseif e[0] =~# '.*powershell$' || e[0] =~# '.*powershell.exe$' + \ || e[0] =~# '.*pwsh$' || e[0] =~# '.*pwsh.exe$' + let str1 = "'cmd \"arg1\" ''arg2'' !%#'" + let str2 = "'cmd \"arg1\" ''arg2'' \\!\\%\\#'" else let str1 = "'cmd \"arg1\" '\\''arg2'\\'' !%#'" let str2 = "'cmd \"arg1\" '\\''arg2'\\'' \\!\\%\\#'" @@ -71,9 +80,14 @@ func Test_shell_options() let [&shellcmdflag, &shellpipe, &shellquote, &shellredir, \ &shellxescape, &shellxquote] = e[1:6] new - r !echo hello - call assert_equal('hello', substitute(getline(2), '\W', '', 'g'), e[0]) - bwipe! + try + r !echo hello + call assert_equal('hello', substitute(getline(2), '\W', '', 'g'), e[0]) + catch + call assert_report('Failed to run shell command, shell: ' .. e[0]) + finally + bwipe! + endtry endif endfor set shell& shellcmdflag& shellpipe& shellquote& @@ -135,6 +149,30 @@ func Test_shellescape() let &shell = save_shell endfunc +" Test for 'shellslash' +func Test_shellslash() + CheckOption shellslash + let save_shellslash = &shellslash + " The shell and cmdflag, and expected slash in tempname with shellslash set or + " unset. The assert checks the file separator before the leafname. + " ".*\\\\[^\\\\]*$" + let shells = [['cmd', '/c', '\\', '/'], + \ ['powershell', '-Command', '\\', '/'], + \ ['pwsh', '-Command', '\\', '/'], + \ ['pwsh', '-c', '\\', '/'], + \ ['sh', '-c', '/', '/']] + for e in shells + exe 'set shell=' .. e[0] .. ' | set shellcmdflag=' .. e[1] + set noshellslash + let file = tempname() + call assert_match('^.\+' .. e[2] .. '[^' .. e[2] .. ']\+$', file, e[0] .. ' ' .. e[1] .. ' nossl') + set shellslash + let file = tempname() + call assert_match('^.\+' .. e[3] .. '[^' .. e[3] .. ']\+$', file, e[0] .. ' ' .. e[1] .. ' ssl') + endfor + let &shellslash = save_shellslash +endfunc + " Test for 'shellxquote' func Test_shellxquote() CheckUnix diff --git a/src/testdir/test_swap.vim b/src/testdir/test_swap.vim index caacd7e175..e62412b10c 100644 --- a/src/testdir/test_swap.vim +++ b/src/testdir/test_swap.vim @@ -360,6 +360,7 @@ func Test_swap_prompt_splitwin() let buf = RunVimInTerminal('', {'rows': 20}) call term_sendkeys(buf, ":set nomore\n") call term_sendkeys(buf, ":set noruler\n") + call term_sendkeys(buf, ":split Xfile1\n") call TermWait(buf) call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort: $', term_getline(buf, 20))}) @@ -371,6 +372,21 @@ func Test_swap_prompt_splitwin() call TermWait(buf) call WaitForAssert({-> assert_match('^1$', term_getline(buf, 20))}) call StopVimInTerminal(buf) + + " This caused Vim to crash when typing "q" at the swap file prompt. + let buf = RunVimInTerminal('-c "au bufadd * let foo_w = wincol()"', {'rows': 18}) + call term_sendkeys(buf, ":e Xfile1\") + call WaitForAssert({-> assert_match('More', term_getline(buf, 18))}) + call term_sendkeys(buf, " ") + call WaitForAssert({-> assert_match('^\[O\]pen Read-Only, (E)dit anyway, (R)ecover, (Q)uit, (A)bort:', term_getline(buf, 18))}) + call term_sendkeys(buf, "q") + call TermWait(buf) + " check that Vim is still running + call term_sendkeys(buf, ":echo 'hello'\") + call WaitForAssert({-> assert_match('^hello', term_getline(buf, 18))}) + call term_sendkeys(buf, ":%bwipe!\") + call StopVimInTerminal(buf) + %bwipe! call delete('Xfile1') endfunc @@ -486,18 +502,18 @@ endfunc " Test for renaming a buffer when the swap file is deleted out-of-band func Test_missing_swap_file() CheckUnix - new Xfile1 + new Xfile2 call delete(swapname('')) - call assert_fails('file Xfile2', 'E301:') - call assert_equal('Xfile2', bufname()) - call assert_true(bufexists('Xfile1')) + call assert_fails('file Xfile3', 'E301:') + call assert_equal('Xfile3', bufname()) call assert_true(bufexists('Xfile2')) + call assert_true(bufexists('Xfile3')) %bw! endfunc " Test for :preserve command func Test_preserve() - new Xfile1 + new Xfile4 setlocal noswapfile call assert_fails('preserve', 'E313:') bw! @@ -505,8 +521,8 @@ endfunc " Test for the v:swapchoice variable func Test_swapchoice() - call writefile(['aaa', 'bbb'], 'Xfile1') - edit Xfile1 + call writefile(['aaa', 'bbb'], 'Xfile5') + edit Xfile5 preserve let swapfname = swapname('') let b = readblob(swapfname) @@ -520,7 +536,7 @@ func Test_swapchoice() autocmd! autocmd SwapExists * let v:swapchoice = 'o' augroup END - edit Xfile1 + edit Xfile5 call assert_true(&readonly) call assert_equal(['aaa', 'bbb'], getline(1, '$')) %bw! @@ -532,11 +548,11 @@ func Test_swapchoice() autocmd SwapExists * let v:swapchoice = 'a' augroup END try - edit Xfile1 + edit Xfile5 catch /^Vim:Interrupt$/ endtry call assert_equal('', @%) - call assert_true(bufexists('Xfile1')) + call assert_true(bufexists('Xfile5')) %bw! call assert_true(filereadable(swapfname)) @@ -545,12 +561,12 @@ func Test_swapchoice() autocmd! autocmd SwapExists * let v:swapchoice = 'd' augroup END - edit Xfile1 - call assert_equal('Xfile1', @%) + edit Xfile5 + call assert_equal('Xfile5', @%) %bw! call assert_false(filereadable(swapfname)) - call delete('Xfile1') + call delete('Xfile5') call delete(swapfname) augroup test_swapchoice autocmd! diff --git a/src/testdir/test_vim9_assign.vim b/src/testdir/test_vim9_assign.vim index 86f890ea09..6bbbc3681e 100644 --- a/src/testdir/test_vim9_assign.vim +++ b/src/testdir/test_vim9_assign.vim @@ -388,6 +388,16 @@ def Test_assign_linebreak() ->copy() END CheckDefFailure(lines, 'E1012:', 2) + + lines =<< trim END + var x: any + x.key = 1 + + 2 + + 3 + + 4 + + 5 + END + CheckDefExecAndScriptFailure2(lines, 'E1148:', 'E1203:', 2) enddef def Test_assign_index() @@ -650,6 +660,37 @@ def Test_assignment_list() d.dd[0] = 0 END CheckDefExecFailure(lines, 'E1147:', 2) + + lines =<< trim END + def OneArg(x: bool) + enddef + def TwoArgs(x: bool, y: bool) + enddef + var fl: list = [OneArg, TwoArgs] + END + CheckDefExecAndScriptFailure(lines, 'E1012:', 5) +enddef + +def PartFuncBool(b: bool): string + return 'done' +enddef + +def Test_assignment_partial() + var lines =<< trim END + var Partial: func(): string = function(PartFuncBool, [true]) + assert_equal('done', Partial()) + END + CheckDefAndScriptSuccess(lines) + + lines =<< trim END + vim9script + def Func(b: bool) + enddef + var Ref: func = function(Func, [true]) + assert_equal('func()', typename(Ref)) + Ref() + END + CheckScriptSuccess(lines) enddef def Test_assignment_list_any_index() diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index c40780f4ae..38bd8271c8 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -180,12 +180,14 @@ def Test_balloon_show() CheckGui CheckFeature balloon_eval + assert_fails('balloon_show(10)', 'E1174:') assert_fails('balloon_show(true)', 'E1174:') enddef def Test_balloon_split() CheckFeature balloon_eval_term + assert_fails('balloon_split([])', 'E1174:') assert_fails('balloon_split(true)', 'E1174:') enddef @@ -206,14 +208,28 @@ def Test_browse() CheckDefExecAndScriptFailure(lines, 'E1174: String required for argument 4') enddef +def Test_bufadd() + assert_fails('bufadd([])', 'E730:') +enddef + def Test_bufexists() - assert_fails('bufexists(true)', 'E1174') + assert_fails('bufexists(true)', 'E1174:') enddef def Test_buflisted() var res: bool = buflisted('asdf') assert_equal(false, res) - assert_fails('buflisted(true)', 'E1174') + assert_fails('buflisted(true)', 'E1174:') + assert_fails('buflisted([])', 'E1174:') +enddef + +def Test_bufload() + assert_fails('bufload([])', 'E730:') +enddef + +def Test_bufloaded() + assert_fails('bufloaded(true)', 'E1174:') + assert_fails('bufloaded([])', 'E1174:') enddef def Test_bufname() @@ -222,6 +238,8 @@ def Test_bufname() edit OtherFile bufname('#')->assert_equal('SomeFile') close + assert_fails('bufname(true)', 'E1138:') + assert_fails('bufname([])', 'E745:') enddef def Test_bufnr() @@ -246,7 +264,19 @@ def Test_bufwinid() bwipe SomeFile bwipe OtherFile - assert_fails('bufwinid(true)', 'E1138') + assert_fails('bufwinid(true)', 'E1138:') + assert_fails('bufwinid([])', 'E745:') +enddef + +def Test_bufwinnr() + assert_fails('bufwinnr(true)', 'E1138:') + assert_fails('bufwinnr([])', 'E745:') +enddef + +def Test_byte2line() + CheckDefFailure(['byte2line("1")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['byte2line([])'], 'E1013: Argument 1: type mismatch, expected number but got list') + assert_equal(-1, byte2line(0)) enddef def Test_call_call() @@ -259,22 +289,29 @@ def Test_ch_logfile() if !has('channel') CheckFeature channel endif - assert_fails('ch_logfile(true)', 'E1174') - assert_fails('ch_logfile("foo", true)', 'E1174') + assert_fails('ch_logfile(true)', 'E1174:') + assert_fails('ch_logfile("foo", true)', 'E1174:') enddef def Test_char2nr() char2nr('あ', true)->assert_equal(12354) - assert_fails('char2nr(true)', 'E1174') + assert_fails('char2nr(true)', 'E1174:') enddef def Test_charclass() - assert_fails('charclass(true)', 'E1174') + assert_fails('charclass(true)', 'E1174:') enddef def Test_chdir() - assert_fails('chdir(true)', 'E1174') + assert_fails('chdir(true)', 'E1174:') +enddef + +def Test_cindent() + CheckDefFailure(['cindent([])'], 'E1013: Argument 1: type mismatch, expected string but got list') + CheckDefFailure(['cindent(null)'], 'E1013: Argument 1: type mismatch, expected string but got special') + assert_equal(-1, cindent(0)) + assert_equal(0, cindent('.')) enddef def Test_clearmatches() @@ -286,7 +323,7 @@ def Test_col() setline(1, 'asdf') col([1, '$'])->assert_equal(5) - assert_fails('col(true)', 'E1174') + assert_fails('col(true)', 'E1174:') enddef def Test_confirm() @@ -294,9 +331,16 @@ def Test_confirm() CheckFeature dialog_con endif - assert_fails('confirm(true)', 'E1174') - assert_fails('confirm("yes", true)', 'E1174') - assert_fails('confirm("yes", "maybe", 2, true)', 'E1174') + assert_fails('confirm(true)', 'E1174:') + assert_fails('confirm("yes", true)', 'E1174:') + assert_fails('confirm("yes", "maybe", 2, true)', 'E1174:') +enddef + +def Test_complete_info() + CheckDefFailure(['complete_info("")'], 'E1013: Argument 1: type mismatch, expected list but got string') + CheckDefFailure(['complete_info({})'], 'E1013: Argument 1: type mismatch, expected list but got dict') + assert_equal({'pum_visible': 0, 'mode': '', 'selected': -1, 'items': []}, complete_info()) + assert_equal({'mode': '', 'items': []}, complete_info(['mode', 'items'])) enddef def Test_copy_return_type() @@ -346,12 +390,32 @@ def Test_delete() assert_equal(true, res) enddef +def Test_diff_filler() + CheckDefFailure(['diff_filler([])'], 'E1013: Argument 1: type mismatch, expected string but got list') + CheckDefFailure(['diff_filler(true)'], 'E1013: Argument 1: type mismatch, expected string but got bool') + assert_equal(0, diff_filler(1)) + assert_equal(0, diff_filler('.')) +enddef + +def Test_escape() + CheckDefFailure(['escape("a", 10)'], 'E1013: Argument 2: type mismatch, expected string but got number') + CheckDefFailure(['escape(10, " ")'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['escape(true, false)'], 'E1013: Argument 1: type mismatch, expected string but got bool') + assert_equal('a\:b', escape("a:b", ":")) +enddef + +def Test_eval() + CheckDefFailure(['eval(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['eval(null)'], 'E1013: Argument 1: type mismatch, expected string but got special') + assert_equal(2, eval('1 + 1')) +enddef + def Test_executable() assert_false(executable("")) assert_false(executable(test_null_string())) - CheckDefExecFailure(['echo executable(123)'], 'E1174:') - CheckDefExecFailure(['echo executable(true)'], 'E1174:') + CheckDefExecFailure(['echo executable(123)'], 'E1013:') + CheckDefExecFailure(['echo executable(true)'], 'E1013:') enddef def Test_execute() @@ -367,11 +431,16 @@ def Test_execute() enddef def Test_exepath() - CheckDefExecFailure(['echo exepath(true)'], 'E1174:') - CheckDefExecFailure(['echo exepath(v:null)'], 'E1174:') + CheckDefExecFailure(['echo exepath(true)'], 'E1013:') + CheckDefExecFailure(['echo exepath(v:null)'], 'E1013:') CheckDefExecFailure(['echo exepath("")'], 'E1175:') enddef +def Test_exists() + CheckDefFailure(['exists(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + call assert_equal(1, exists('&tabstop')) +enddef + def Test_expand() split SomeFile expand('%', true, true)->assert_equal(['SomeFile']) @@ -507,6 +576,16 @@ def Test_extend_with_error_function() CheckScriptFailure(lines, 'E1001: Variable not found: m') enddef +def Test_feedkeys() + CheckDefFailure(['feedkeys(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['feedkeys("x", 10)'], 'E1013: Argument 2: type mismatch, expected string but got number') + CheckDefFailure(['feedkeys([], {})'], 'E1013: Argument 1: type mismatch, expected string but got list') + g:TestVar = 1 + feedkeys(":g:TestVar = 789\n", 'xt') + assert_equal(789, g:TestVar) + unlet g:TestVar +enddef + def Test_job_info_return_type() if has('job') job_start(&shell) @@ -521,16 +600,16 @@ def Test_filereadable() assert_false(filereadable("")) assert_false(filereadable(test_null_string())) - CheckDefExecFailure(['echo filereadable(123)'], 'E1174:') - CheckDefExecFailure(['echo filereadable(true)'], 'E1174:') + CheckDefExecFailure(['echo filereadable(123)'], 'E1013:') + CheckDefExecFailure(['echo filereadable(true)'], 'E1013:') enddef def Test_filewritable() assert_false(filewritable("")) assert_false(filewritable(test_null_string())) - CheckDefExecFailure(['echo filewritable(123)'], 'E1174:') - CheckDefExecFailure(['echo filewritable(true)'], 'E1174:') + CheckDefExecFailure(['echo filewritable(123)'], 'E1013:') + CheckDefExecFailure(['echo filewritable(true)'], 'E1013:') enddef def Test_finddir() @@ -620,15 +699,20 @@ def Test_float_funcs_args() CheckDefFailure(['echo trunc("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') enddef +def Test_fnameescape() + CheckDefFailure(['fnameescape(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_equal('\+a\%b\|', fnameescape('+a%b|')) +enddef + def Test_fnamemodify() CheckDefSuccess(['echo fnamemodify(test_null_string(), ":p")']) CheckDefSuccess(['echo fnamemodify("", ":p")']) CheckDefSuccess(['echo fnamemodify("file", test_null_string())']) CheckDefSuccess(['echo fnamemodify("file", "")']) - CheckDefExecFailure(['echo fnamemodify(true, ":p")'], 'E1174: String required for argument 1') - CheckDefExecFailure(['echo fnamemodify(v:null, ":p")'], 'E1174: String required for argument 1') - CheckDefExecFailure(['echo fnamemodify("file", true)'], 'E1174: String required for argument 2') + CheckDefExecFailure(['echo fnamemodify(true, ":p")'], 'E1013: Argument 1: type mismatch, expected string but got bool') + CheckDefExecFailure(['echo fnamemodify(v:null, ":p")'], 'E1013: Argument 1: type mismatch, expected string but got special') + CheckDefExecFailure(['echo fnamemodify("file", true)'], 'E1013: Argument 2: type mismatch, expected string but got bool') enddef def Wrong_dict_key_type(items: list): list @@ -654,6 +738,30 @@ def Test_filter_missing_argument() res->assert_equal({aa: [1], ac: [3]}) enddef +def Test_foldclosed() + CheckDefFailure(['foldclosed(function("min"))'], 'E1013: Argument 1: type mismatch, expected string but got func(...): any') + assert_equal(-1, foldclosed(1)) + assert_equal(-1, foldclosed('$')) +enddef + +def Test_foldclosedend() + CheckDefFailure(['foldclosedend(true)'], 'E1013: Argument 1: type mismatch, expected string but got bool') + assert_equal(-1, foldclosedend(1)) + assert_equal(-1, foldclosedend('w0')) +enddef + +def Test_foldlevel() + CheckDefFailure(['foldlevel(0z10)'], 'E1013: Argument 1: type mismatch, expected string but got blob') + assert_equal(0, foldlevel(1)) + assert_equal(0, foldlevel('.')) +enddef + +def Test_foldtextresult() + CheckDefFailure(['foldtextresult(1.1)'], 'E1013: Argument 1: type mismatch, expected string but got float') + assert_equal('', foldtextresult(1)) + assert_equal('', foldtextresult('.')) +enddef + def Test_fullcommand() assert_equal('next', fullcommand('n')) assert_equal('noremap', fullcommand('no')) @@ -762,36 +870,40 @@ def Test_getloclist_return_type() d->assert_equal({items: []}) enddef +def Test_getfontname() + CheckDefFailure(['getfontname(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') +enddef + def Test_getfperm() assert_equal('', getfperm("")) assert_equal('', getfperm(test_null_string())) - CheckDefExecFailure(['echo getfperm(true)'], 'E1174:') - CheckDefExecFailure(['echo getfperm(v:null)'], 'E1174:') + CheckDefExecFailure(['echo getfperm(true)'], 'E1013:') + CheckDefExecFailure(['echo getfperm(v:null)'], 'E1013:') enddef def Test_getfsize() assert_equal(-1, getfsize("")) assert_equal(-1, getfsize(test_null_string())) - CheckDefExecFailure(['echo getfsize(true)'], 'E1174:') - CheckDefExecFailure(['echo getfsize(v:null)'], 'E1174:') + CheckDefExecFailure(['echo getfsize(true)'], 'E1013:') + CheckDefExecFailure(['echo getfsize(v:null)'], 'E1013:') enddef def Test_getftime() assert_equal(-1, getftime("")) assert_equal(-1, getftime(test_null_string())) - CheckDefExecFailure(['echo getftime(true)'], 'E1174:') - CheckDefExecFailure(['echo getftime(v:null)'], 'E1174:') + CheckDefExecFailure(['echo getftime(true)'], 'E1013:') + CheckDefExecFailure(['echo getftime(v:null)'], 'E1013:') enddef def Test_getftype() assert_equal('', getftype("")) assert_equal('', getftype(test_null_string())) - CheckDefExecFailure(['echo getftype(true)'], 'E1174:') - CheckDefExecFailure(['echo getftype(v:null)'], 'E1174:') + CheckDefExecFailure(['echo getftype(true)'], 'E1013:') + CheckDefExecFailure(['echo getftype(v:null)'], 'E1013:') enddef def Test_getjumplist() @@ -800,10 +912,27 @@ def Test_getjumplist() CheckDefFailure(['echo getjumplist(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') enddef +def Test_getmarklist() + CheckDefFailure(['getmarklist([])'], 'E1013: Argument 1: type mismatch, expected string but got list') + assert_equal([], getmarklist(10000)) + assert_fails('getmarklist("a%b@#")', 'E94:') +enddef + def Test_getmatches() CheckDefFailure(['echo getmatches("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') enddef +def Test_getpos() + CheckDefFailure(['getpos(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_equal([0, 1, 1, 0], getpos('.')) + assert_equal([0, 0, 0, 0], getpos('a')) +enddef + +def Test_getqflist() + CheckDefFailure(['getqflist([])'], 'E1013: Argument 1: type mismatch, expected dict but got list') + call assert_equal({}, getqflist({})) +enddef + def Test_getqflist_return_type() var l = getqflist() l->assert_equal([]) @@ -847,6 +976,11 @@ def Test_gettagstack() CheckDefFailure(['echo gettagstack("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') enddef +def Test_gettext() + CheckDefFailure(['gettext(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_equal('abc', gettext("abc")) +enddef + def Test_getwininfo() CheckDefFailure(['echo getwininfo("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') enddef @@ -859,6 +993,11 @@ def Test_glob() glob('runtest.vim', true, true, true)->assert_equal(['runtest.vim']) enddef +def Test_glob2regpat() + CheckDefFailure(['glob2regpat(null)'], 'E1013: Argument 1: type mismatch, expected string but got special') + assert_equal('^$', glob2regpat('')) +enddef + def Test_globpath() globpath('.', 'runtest.vim', true, true, true)->assert_equal(['./runtest.vim']) enddef @@ -880,10 +1019,56 @@ def Test_hasmapto() iunabbrev foo enddef +def Test_histadd() + CheckDefFailure(['histadd(1, "x")'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['histadd(":", 10)'], 'E1013: Argument 2: type mismatch, expected string but got number') + histadd("search", 'skyblue') + assert_equal('skyblue', histget('/', -1)) +enddef + +def Test_histnr() + CheckDefFailure(['histnr(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_equal(-1, histnr('abc')) +enddef + +def Test_hlID() + CheckDefFailure(['hlID(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_equal(0, hlID('NonExistingHighlight')) +enddef + +def Test_hlexists() + CheckDefFailure(['hlexists([])'], 'E1013: Argument 1: type mismatch, expected string but got list') + assert_equal(0, hlexists('NonExistingHighlight')) +enddef + +def Test_iconv() + CheckDefFailure(['iconv(1, "from", "to")'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['iconv("abc", 10, "to")'], 'E1013: Argument 2: type mismatch, expected string but got number') + CheckDefFailure(['iconv("abc", "from", 20)'], 'E1013: Argument 3: type mismatch, expected string but got number') + assert_equal('abc', iconv('abc', 'fromenc', 'toenc')) +enddef + def Test_index() index(['a', 'b', 'a', 'B'], 'b', 2, true)->assert_equal(3) enddef +def Test_inputlist() + CheckDefFailure(['inputlist(10)'], 'E1013: Argument 1: type mismatch, expected list but got number') + CheckDefFailure(['inputlist("abc")'], 'E1013: Argument 1: type mismatch, expected list but got string') + CheckDefFailure(['inputlist([1, 2, 3])'], 'E1013: Argument 1: type mismatch, expected list but got list') + feedkeys("2\", 't') + var r: number = inputlist(['a', 'b', 'c']) + assert_equal(2, r) +enddef + +def Test_inputsecret() + CheckDefFailure(['inputsecret(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['inputsecret("Pass:", 20)'], 'E1013: Argument 2: type mismatch, expected string but got number') + feedkeys("\", 't') + var ans: string = inputsecret('Pass:', '123') + assert_equal('123', ans) +enddef + let s:number_one = 1 let s:number_two = 2 let s:string_keep = 'keep' @@ -928,13 +1113,51 @@ def Test_invert() CheckDefFailure(['echo invert("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') enddef +def Test_isdirectory() + CheckDefFailure(['isdirectory(1.1)'], 'E1013: Argument 1: type mismatch, expected string but got float') + assert_false(isdirectory('NonExistingDir')) +enddef + +def Test_items() + CheckDefFailure(['[]->items()'], 'E1013: Argument 1: type mismatch, expected dict but got list') + assert_equal([['a', 10], ['b', 20]], {'a': 10, 'b': 20}->items()) + assert_equal([], {}->items()) +enddef + +def Test_js_decode() + CheckDefFailure(['js_decode(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_equal([1, 2], js_decode('[1,2]')) +enddef + +def Test_json_decode() + CheckDefFailure(['json_decode(true)'], 'E1013: Argument 1: type mismatch, expected string but got bool') + assert_equal(1.0, json_decode('1.0')) +enddef + +def Test_keys() + CheckDefFailure(['keys([])'], 'E1013: Argument 1: type mismatch, expected dict but got list') + assert_equal(['a'], {a: 'v'}->keys()) + assert_equal([], {}->keys()) +enddef + def Test_keys_return_type() const var: list = {a: 1, b: 2}->keys() var->assert_equal(['a', 'b']) enddef def Test_line() - assert_fails('line(true)', 'E1174') + assert_fails('line(true)', 'E1174:') +enddef + +def Test_line2byte() + CheckDefFailure(['line2byte(true)'], 'E1013: Argument 1: type mismatch, expected string but got bool') + assert_equal(-1, line2byte(1)) + assert_equal(-1, line2byte(10000)) +enddef + +def Test_lispindent() + CheckDefFailure(['lispindent({})'], 'E1013: Argument 1: type mismatch, expected string but got dict') + assert_equal(0, lispindent(1)) enddef def Test_list2str_str2list_utf8() @@ -1080,6 +1303,13 @@ def Test_max() assert_equal([4, 5], l2) enddef +def Test_menu_info() + CheckDefFailure(['menu_info(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['menu_info(10, "n")'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['menu_info("File", 10)'], 'E1013: Argument 2: type mismatch, expected string but got number') + assert_equal({}, menu_info('aMenu')) +enddef + def Test_min() g:flag = true var l1: list = g:flag @@ -1094,6 +1324,11 @@ def Test_min() assert_equal([4, 5], l2) enddef +def Test_nextnonblank() + CheckDefFailure(['nextnonblank(null)'], 'E1013: Argument 1: type mismatch, expected string but got special') + assert_equal(0, nextnonblank(1)) +enddef + def Test_nr2char() nr2char(97, true)->assert_equal('a') enddef @@ -1103,6 +1338,25 @@ def Test_or() CheckDefFailure(['echo or(0x1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') enddef +def Test_prevnonblank() + CheckDefFailure(['prevnonblank(null)'], 'E1013: Argument 1: type mismatch, expected string but got special') + assert_equal(0, prevnonblank(1)) +enddef + +def Test_prompt_getprompt() + if has('channel') + CheckDefFailure(['prompt_getprompt([])'], 'E1013: Argument 1: type mismatch, expected string but got list') + assert_equal('', prompt_getprompt('NonExistingBuf')) + endif +enddef + +def Test_rand() + CheckDefFailure(['rand(10)'], 'E1013: Argument 1: type mismatch, expected list but got number') + CheckDefFailure(['rand(["a"])'], 'E1013: Argument 1: type mismatch, expected list but got list') + assert_true(rand() >= 0) + assert_true(rand(srand()) >= 0) +enddef + def Test_readdir() eval expand('sautest')->readdir((e) => e[0] !=# '.') eval expand('sautest')->readdirex((e) => e.name[0] !=# '.') @@ -1134,6 +1388,41 @@ def Test_readfile() delete('Xreadfile') enddef +def Test_reltime() + CheckDefFailure(['reltime("x")'], 'E1013: Argument 1: type mismatch, expected list but got string') + CheckDefFailure(['reltime(["x", "y"])'], 'E1013: Argument 1: type mismatch, expected list but got list') + CheckDefFailure(['reltime([1, 2], 10)'], 'E1013: Argument 2: type mismatch, expected list but got number') + CheckDefFailure(['reltime([1, 2], ["a", "b"])'], 'E1013: Argument 2: type mismatch, expected list but got list') + var start: list = reltime() + assert_true(type(reltime(start)) == v:t_list) + var end: list = reltime() + assert_true(type(reltime(start, end)) == v:t_list) +enddef + +def Test_reltimefloat() + CheckDefFailure(['reltimefloat("x")'], 'E1013: Argument 1: type mismatch, expected list but got string') + CheckDefFailure(['reltimefloat([1.1])'], 'E1013: Argument 1: type mismatch, expected list but got list') + assert_true(type(reltimefloat(reltime())) == v:t_float) +enddef + +def Test_reltimestr() + CheckDefFailure(['reltimestr(true)'], 'E1013: Argument 1: type mismatch, expected list but got bool') + CheckDefFailure(['reltimestr([true])'], 'E1013: Argument 1: type mismatch, expected list but got list') + assert_true(type(reltimestr(reltime())) == v:t_string) +enddef + +def Test_remote_foreground() + CheckFeature clientserver + # remote_foreground() doesn't fail on MS-Windows + CheckNotMSWindows + CheckDefFailure(['remote_foreground(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_fails('remote_foreground("NonExistingServer")', 'E241:') +enddef + +def Test_remote_startserver() + CheckDefFailure(['remote_startserver({})'], 'E1013: Argument 1: type mismatch, expected string but got dict') +enddef + def Test_remove_return_type() var l = remove({one: [1, 2], two: [3, 4]}, 'one') var res = 0 @@ -1143,6 +1432,16 @@ def Test_remove_return_type() res->assert_equal(3) enddef +def Test_rename() + CheckDefFailure(['rename(1, "b")'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['rename("a", 2)'], 'E1013: Argument 2: type mismatch, expected string but got number') +enddef + +def Test_resolve() + CheckDefFailure(['resolve([])'], 'E1013: Argument 1: type mismatch, expected string but got list') + assert_equal('SomeFile', resolve('SomeFile')) +enddef + def Test_reverse_return_type() var l = reverse([1, 2, 3]) var res = 0 @@ -1167,6 +1466,13 @@ def Test_screenchars() CheckDefFailure(['echo screenchars(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') enddef +def Test_screenpos() + CheckDefFailure(['screenpos("a", 1, 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['screenpos(1, "b", 1)'], 'E1013: Argument 2: type mismatch, expected number but got string') + CheckDefFailure(['screenpos(1, 1, "c")'], 'E1013: Argument 3: type mismatch, expected number but got string') + assert_equal({col: 1, row: 1, endcol: 1, curscol: 1}, screenpos(1, 1, 1)) +enddef + def Test_screenstring() CheckDefFailure(['echo screenstring("x", 1)'], 'E1013: Argument 1: type mismatch, expected number but got string') CheckDefFailure(['echo screenstring(1, "x")'], 'E1013: Argument 2: type mismatch, expected number but got string') @@ -1334,10 +1640,23 @@ def Test_setbufvar() getbufvar('%', 'myvar')->assert_equal(123) enddef +def Test_setcharsearch() + CheckDefFailure(['setcharsearch("x")'], 'E1013: Argument 1: type mismatch, expected dict but got string') + CheckDefFailure(['setcharsearch([])'], 'E1013: Argument 1: type mismatch, expected dict but got list') + var d: dict = {char: 'x', forward: 1, until: 1} + setcharsearch(d) + assert_equal(d, getcharsearch()) +enddef + def Test_setcmdpos() CheckDefFailure(['echo setcmdpos("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') enddef +def Test_setfperm() + CheckDefFailure(['setfperm(1, "b")'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['setfperm("a", 0z10)'], 'E1013: Argument 2: type mismatch, expected string but got blob') +enddef + def Test_setloclist() var items = [{filename: '/tmp/file', lnum: 1, valid: true}] var what = {items: items} @@ -1353,10 +1672,21 @@ def Test_setreg() assert_fails('setreg("ab", 0)', 'E1162:') enddef +def Test_sha256() + CheckDefFailure(['sha256(100)'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['sha256(0zABCD)'], 'E1013: Argument 1: type mismatch, expected string but got blob') + assert_equal('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad', sha256('abc')) +enddef + def Test_shiftwidth() CheckDefFailure(['echo shiftwidth("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') enddef +def Test_simplify() + CheckDefFailure(['simplify(100)'], 'E1013: Argument 1: type mismatch, expected string but got number') + call assert_equal('NonExistingFile', simplify('NonExistingFile')) +enddef + def Test_slice() assert_equal('12345', slice('012345', 1)) assert_equal('123', slice('012345', 1, 4)) @@ -1388,6 +1718,16 @@ def Test_spellsuggest() endif enddef +def Test_sound_stop() + CheckFeature sound + CheckDefFailure(['sound_stop("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_soundfold() + CheckDefFailure(['soundfold(20)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_equal('abc', soundfold('abc')) +enddef + def Test_sort_return_type() var res: list res = [1, 2, 3]->sort() @@ -1408,10 +1748,25 @@ def Test_sort_argument() CheckDefAndScriptSuccess(lines) enddef +def Test_spellbadword() + CheckDefFailure(['spellbadword(100)'], 'E1013: Argument 1: type mismatch, expected string but got number') + spellbadword('good')->assert_equal(['', '']) +enddef + def Test_split() split(' aa bb ', '\W\+', true)->assert_equal(['', 'aa', 'bb', '']) enddef +def Test_srand() + CheckDefFailure(['srand("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + type(srand(100))->assert_equal(v:t_list) +enddef + +def Test_state() + CheckDefFailure(['state({})'], 'E1013: Argument 1: type mismatch, expected string but got dict') + assert_equal('', state('a')) +enddef + def Run_str2float() if !has('float') MissingFeature 'float' @@ -1439,6 +1794,33 @@ def Test_strchars() strchars("A\u20dd", true)->assert_equal(1) enddef +def Test_strlen() + CheckDefFailure(['strlen([])'], 'E1013: Argument 1: type mismatch, expected string but got list') + "abc"->strlen()->assert_equal(3) + strlen(99)->assert_equal(2) +enddef + +def Test_strptime() + CheckFunction strptime + CheckDefFailure(['strptime(10, "2021")'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckDefFailure(['strptime("%Y", 2021)'], 'E1013: Argument 2: type mismatch, expected string but got number') + # BUG: Directly calling strptime() in this function gives an "E117: Unknown + # function" error on MS-Windows even with the above CheckFunction call for + # strptime(). + #assert_true(strptime('%Y', '2021') != 0) +enddef + +def Test_strtrans() + CheckDefFailure(['strtrans(20)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_equal('abc', strtrans('abc')) +enddef + +def Test_strwidth() + CheckDefFailure(['strwidth(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + CheckScriptFailure(['vim9script', 'echo strwidth(10)'], 'E1024:') + assert_equal(4, strwidth('abcd')) +enddef + def Test_submatch() var pat = 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)' var Rep = () => range(10)->mapnew((_, v) => submatch(v, true))->string() @@ -1457,6 +1839,16 @@ def Test_substitute() endif enddef +def Test_swapinfo() + CheckDefFailure(['swapinfo({})'], 'E1013: Argument 1: type mismatch, expected string but got dict') + call assert_equal({error: 'Cannot open file'}, swapinfo('x')) +enddef + +def Test_swapname() + CheckDefFailure(['swapname([])'], 'E1013: Argument 1: type mismatch, expected string but got list') + assert_fails('swapname("NonExistingBuf")', 'E94:') +enddef + def Test_synID() new setline(1, "text") @@ -1464,6 +1856,22 @@ def Test_synID() bwipe! enddef +def Test_synIDtrans() + CheckDefFailure(['synIDtrans("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') +enddef + +def Test_tabpagebuflist() + CheckDefFailure(['tabpagebuflist("t")'], 'E1013: Argument 1: type mismatch, expected number but got string') + assert_equal([bufnr('')], tabpagebuflist()) + assert_equal([bufnr('')], tabpagebuflist(1)) +enddef + +def Test_tabpagenr() + CheckDefFailure(['tabpagenr(1)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_equal(1, tabpagenr('$')) + assert_equal(1, tabpagenr()) +enddef + def Test_term_gettty() if !has('terminal') MissingFeature 'terminal' @@ -1486,6 +1894,12 @@ def Test_term_start() endif enddef +def Test_timer_info() + CheckDefFailure(['timer_info("id")'], 'E1013: Argument 1: type mismatch, expected number but got string') + assert_equal([], timer_info(100)) + assert_equal([], timer_info()) +enddef + def Test_timer_paused() var id = timer_start(50, () => 0) timer_pause(id, true) @@ -1494,6 +1908,11 @@ def Test_timer_paused() timer_stop(id) enddef +def Test_timer_stop() + CheckDefFailure(['timer_stop("x")'], 'E1013: Argument 1: type mismatch, expected number but got string') + assert_equal(0, timer_stop(100)) +enddef + def Test_tolower() CheckDefFailure(['echo tolower(1)'], 'E1013: Argument 1: type mismatch, expected string but got number') enddef @@ -1508,17 +1927,46 @@ def Test_tr() CheckDefFailure(['echo tr("a", "a", 1)'], 'E1013: Argument 3: type mismatch, expected string but got number') enddef +def Test_undofile() + CheckDefFailure(['undofile(10)'], 'E1013: Argument 1: type mismatch, expected string but got number') + assert_equal('.abc.un~', fnamemodify(undofile('abc'), ':t')) +enddef + +def Test_values() + CheckDefFailure(['values([])'], 'E1013: Argument 1: type mismatch, expected dict but got list') + assert_equal([], {}->values()) + assert_equal(['sun'], {star: 'sun'}->values()) +enddef + def Test_win_execute() assert_equal("\n" .. winnr(), win_execute(win_getid(), 'echo winnr()')) assert_equal('', win_execute(342343, 'echo winnr()')) enddef +def Test_win_findbuf() + CheckDefFailure(['win_findbuf("a")'], 'E1013: Argument 1: type mismatch, expected number but got string') + assert_equal([], win_findbuf(1000)) + assert_equal([win_getid()], win_findbuf(bufnr(''))) +enddef + +def Test_win_getid() + CheckDefFailure(['win_getid(".")'], 'E1013: Argument 1: type mismatch, expected number but got string') + CheckDefFailure(['win_getid(1, ".")'], 'E1013: Argument 2: type mismatch, expected number but got string') + assert_equal(win_getid(), win_getid(1, 1)) +enddef + def Test_win_splitmove() split win_splitmove(1, 2, {vertical: true, rightbelow: true}) close enddef +def Test_winnr() + CheckDefFailure(['winnr([])'], 'E1013: Argument 1: type mismatch, expected string but got list') + assert_equal(1, winnr()) + assert_equal(1, winnr('$')) +enddef + def Test_winrestcmd() split var cmd = winrestcmd() @@ -1528,6 +1976,14 @@ def Test_winrestcmd() close enddef +def Test_winrestview() + CheckDefFailure(['winrestview([])'], 'E1013: Argument 1: type mismatch, expected dict but got list') + :%d _ + setline(1, 'Hello World') + winrestview({lnum: 1, col: 6}) + assert_equal([1, 7], [line('.'), col('.')]) +enddef + def Test_winsaveview() var view: dict = winsaveview() diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 9170cc9563..1e99e9568d 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -57,7 +57,7 @@ def Test_expr1_trinary() assert_equal(function('len'), Res) var RetOne: func(string): number = function('len') - var RetTwo: func(string): number = function('winnr') + var RetTwo: func(string): number = function('charcol') var RetThat: func = g:atrue ? RetOne : RetTwo assert_equal(function('len'), RetThat) @@ -2970,13 +2970,6 @@ def Test_expr7_method_call() END CheckDefAndScriptSuccess(lines) - lines =<< trim END - def RetVoid() - enddef - RetVoid()->byte2line() - END - CheckDefExecAndScriptFailure(lines, 'E1031:') - lines =<< trim END def RetVoid() enddef diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 4816cb6674..89cdee7437 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -452,6 +452,12 @@ def Test_call_default_args() MyDefaultThird('->', 'xx', v:none)->assert_equal('->xxbb') MyDefaultThird('->', v:none, 'yy')->assert_equal('->aayy') MyDefaultThird('->', 'xx', 'yy')->assert_equal('->xxyy') + + def DefArg(mandatory: any, optional = mandatory): string + return mandatory .. optional + enddef + DefArg(1234)->assert_equal('12341234') + DefArg("ok")->assert_equal('okok') END CheckDefAndScriptSuccess(lines) @@ -1024,7 +1030,7 @@ def Test_pass_legacy_lambda_to_def_func() lines =<< trim END vim9script - def g:TestFunc(f: func()) + def g:TestFunc(f: func) enddef legacy call g:TestFunc({-> 0}) delfunc g:TestFunc diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 3ff34a2475..9cf098917c 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -603,6 +603,52 @@ def Test_try_catch_throw() CheckScriptSuccess(lines) assert_match('E808: Number or Float required', g:caught) unlet g:caught + + # missing catch and/or finally + lines =<< trim END + vim9script + try + echo 'something' + endtry + END + CheckScriptFailure(lines, 'E1032:') +enddef + +def Test_try_in_catch() + var lines =<< trim END + vim9script + var seq = [] + def DoIt() + try + seq->add('throw 1') + eval [][0] + seq->add('notreached') + catch + seq->add('catch') + try + seq->add('throw 2') + eval [][0] + seq->add('notreached') + catch /nothing/ + seq->add('notreached') + endtry + seq->add('done') + endtry + enddef + DoIt() + assert_equal(['throw 1', 'catch', 'throw 2', 'done'], seq) + END +enddef + +def Test_error_in_catch() + var lines =<< trim END + try + eval [][0] + catch /E684:/ + eval [][0] + endtry + END + CheckDefExecFailure(lines, 'E684:', 4) enddef " :while at the very start of a function that :continue jumps to @@ -742,6 +788,49 @@ def Test_try_catch_nested() assert_equal('intry', ReturnFinally()) assert_equal('finally', g:in_finally) + + var l = [] + try + l->add('1') + throw 'bad' + l->add('x') + catch /bad/ + l->add('2') + try + l->add('3') + throw 'one' + l->add('x') + catch /one/ + l->add('4') + try + l->add('5') + throw 'more' + l->add('x') + catch /more/ + l->add('6') + endtry + endtry + endtry + assert_equal(['1', '2', '3', '4', '5', '6'], l) + + l = [] + try + try + l->add('1') + throw 'foo' + l->add('x') + catch + l->add('2') + throw 'bar' + l->add('x') + finally + l->add('3') + endtry + l->add('x') + catch /bar/ + l->add('4') + endtry + assert_equal(['1', '2', '3', '4'], l) enddef def TryOne(): number diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim index fb26750829..10929519b4 100644 --- a/src/testdir/test_visual.vim +++ b/src/testdir/test_visual.vim @@ -1,6 +1,8 @@ " Tests for various Visual modes. source shared.vim +source check.vim +source screendump.vim func Test_block_shift_multibyte() " Uses double-wide character. @@ -806,11 +808,7 @@ func Test_visual_block_mode() %d _ call setline(1, ['aaa', 'bbb', 'ccc']) exe "normal $\2jA\x" - " BUG: Instead of adding x as the third character in all the three lines, - " 'a' is added in the second and third lines at the end. This bug is not - " reproducible if this operation is performed manually. - "call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) - call assert_equal(['aaxa', 'bbba', 'ccca'], getline(1, '$')) + call assert_equal(['aaxa', 'bbxb', 'ccxc'], getline(1, '$')) " Repeat the previous test but use 'l' to move the cursor instead of '$' call setline(1, ['aaa', 'bbb', 'ccc']) exe "normal! gg2l\2jA\x" @@ -1225,5 +1223,47 @@ func Test_visual_put_in_block_using_zy_and_zp() bwipe! endfunc +func Test_visual_put_blockedit_zy_and_zp() + new + + call setline(1, ['aa', 'bbbbb', 'ccc', '', 'XX', 'GGHHJ', 'RTZU']) + exe "normal! gg0\2j$zy" + norm! 5gg0zP + call assert_equal(['aa', 'bbbbb', 'ccc', '', 'aaXX', 'bbbbbGGHHJ', 'cccRTZU'], getline(1, 7)) + " + " now with blockmode editing + sil %d + :set ve=block + call setline(1, ['aa', 'bbbbb', 'ccc', '', 'XX', 'GGHHJ', 'RTZU']) + exe "normal! gg0\2j$zy" + norm! 5gg0zP + call assert_equal(['aa', 'bbbbb', 'ccc', '', 'aaXX', 'bbbbbGGHHJ', 'cccRTZU'], getline(1, 7)) + set ve&vim + bw! +endfunc + +func Test_visual_block_with_virtualedit() + CheckScreendump + + let lines =<< trim END + call setline(1, ['aaaaaa', 'bbbb', 'cc']) + set virtualedit=block + normal G + END + call writefile(lines, 'XTest_block') + + let buf = RunVimInTerminal('-S XTest_block', {'rows': 8, 'cols': 50}) + call term_sendkeys(buf, "\gg$") + call VerifyScreenDump(buf, 'Test_visual_block_with_virtualedit', {}) + + call term_sendkeys(buf, "\gg\G$") + call VerifyScreenDump(buf, 'Test_visual_block_with_virtualedit2', {}) + + " clean up + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) + call delete('XTest_block') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_xxd.vim b/src/testdir/test_xxd.vim index 4eef7a4e53..922c5ab3a6 100644 --- a/src/testdir/test_xxd.vim +++ b/src/testdir/test_xxd.vim @@ -226,7 +226,7 @@ endfunc func Test_xxd_version() new exe 'r! ' . s:xxd_cmd . ' -v' - call assert_match("xxd V1.10 .* by Juergen Weigert", join(getline(1, 3))) + call assert_match('xxd 20\d\d-\d\d-\d\d by Juergen Weigert et al\.', join(getline(1, 3))) bwipe! endfunc diff --git a/src/ui.c b/src/ui.c index b9d211979b..478ac3ae07 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1105,7 +1105,7 @@ ui_cursor_shape_forced(int forced) # endif # ifdef FEAT_CONCEAL - conceal_check_cursor_line(); + conceal_check_cursor_line(FALSE); # endif } diff --git a/src/userfunc.c b/src/userfunc.c index 06fca0cf01..4234681cd7 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3103,6 +3103,7 @@ call_func( int argv_clear = 0; int argv_base = 0; partial_T *partial = funcexe->partial; + type_T check_type; // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) // even when call_func() returns FAIL. @@ -3146,6 +3147,17 @@ call_func( argv[i + argv_clear] = argvars_in[i]; argvars = argv; argcount = partial->pt_argc + argcount_in; + + if (funcexe->check_type != NULL + && funcexe->check_type->tt_argcount != -1) + { + // Now funcexe->check_type is missing the added arguments, make + // a copy of the type with the correction. + check_type = *funcexe->check_type; + funcexe->check_type = &check_type; + check_type.tt_argcount += partial->pt_argc; + check_type.tt_min_argcount += partial->pt_argc; + } } } @@ -5478,35 +5490,32 @@ find_var_in_scoped_ht(char_u *name, int no_autoload) int set_ref_in_previous_funccal(int copyID) { - int abort = FALSE; funccall_T *fc; - for (fc = previous_funccal; !abort && fc != NULL; fc = fc->caller) + for (fc = previous_funccal; fc != NULL; fc = fc->caller) { fc->fc_copyID = copyID + 1; - abort = abort - || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL) - || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL) - || set_ref_in_list_items(&fc->l_varlist, copyID + 1, NULL); + if (set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, NULL) + || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, NULL) + || set_ref_in_list_items(&fc->l_varlist, copyID + 1, NULL)) + return TRUE; } - return abort; + return FALSE; } static int set_ref_in_funccal(funccall_T *fc, int copyID) { - int abort = FALSE; - if (fc->fc_copyID != copyID) { fc->fc_copyID = copyID; - abort = abort - || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL) - || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL) - || set_ref_in_list_items(&fc->l_varlist, copyID, NULL) - || set_ref_in_func(NULL, fc->func, copyID); + if (set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL) + || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL) + || set_ref_in_list_items(&fc->l_varlist, copyID, NULL) + || set_ref_in_func(NULL, fc->func, copyID)) + return TRUE; } - return abort; + return FALSE; } /* @@ -5515,19 +5524,19 @@ set_ref_in_funccal(funccall_T *fc, int copyID) int set_ref_in_call_stack(int copyID) { - int abort = FALSE; funccall_T *fc; funccal_entry_T *entry; - for (fc = current_funccal; !abort && fc != NULL; fc = fc->caller) - abort = abort || set_ref_in_funccal(fc, copyID); + for (fc = current_funccal; fc != NULL; fc = fc->caller) + if (set_ref_in_funccal(fc, copyID)) + return TRUE; // Also go through the funccal_stack. - for (entry = funccal_stack; !abort && entry != NULL; entry = entry->next) - for (fc = entry->top_funccal; !abort && fc != NULL; fc = fc->caller) - abort = abort || set_ref_in_funccal(fc, copyID); - - return abort; + for (entry = funccal_stack; entry != NULL; entry = entry->next) + for (fc = entry->top_funccal; fc != NULL; fc = fc->caller) + if (set_ref_in_funccal(fc, copyID)) + return TRUE; + return FALSE; } /* @@ -5538,7 +5547,6 @@ set_ref_in_functions(int copyID) { int todo; hashitem_T *hi = NULL; - int abort = FALSE; ufunc_T *fp; todo = (int)func_hashtab.ht_used; @@ -5548,11 +5556,12 @@ set_ref_in_functions(int copyID) { --todo; fp = HI2UF(hi); - if (!func_name_refcount(fp->uf_name)) - abort = abort || set_ref_in_func(NULL, fp, copyID); + if (!func_name_refcount(fp->uf_name) + && set_ref_in_func(NULL, fp, copyID)) + return TRUE; } } - return abort; + return FALSE; } /* @@ -5562,12 +5571,12 @@ set_ref_in_functions(int copyID) set_ref_in_func_args(int copyID) { int i; - int abort = FALSE; for (i = 0; i < funcargs.ga_len; ++i) - abort = abort || set_ref_in_item(((typval_T **)funcargs.ga_data)[i], - copyID, NULL, NULL); - return abort; + if (set_ref_in_item(((typval_T **)funcargs.ga_data)[i], + copyID, NULL, NULL)) + return TRUE; + return FALSE; } /* diff --git a/src/version.c b/src/version.c index e208dc5e0d..c481f28d13 100644 --- a/src/version.c +++ b/src/version.c @@ -770,6 +770,80 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 3107, +/**/ + 3106, +/**/ + 3105, +/**/ + 3104, +/**/ + 3103, +/**/ + 3102, +/**/ + 3101, +/**/ + 3100, +/**/ + 3099, +/**/ + 3098, +/**/ + 3097, +/**/ + 3096, +/**/ + 3095, +/**/ + 3094, +/**/ + 3093, +/**/ + 3092, +/**/ + 3091, +/**/ + 3090, +/**/ + 3089, +/**/ + 3088, +/**/ + 3087, +/**/ + 3086, +/**/ + 3085, +/**/ + 3084, +/**/ + 3083, +/**/ + 3082, +/**/ + 3081, +/**/ + 3080, +/**/ + 3079, +/**/ + 3078, +/**/ + 3077, +/**/ + 3076, +/**/ + 3075, +/**/ + 3074, +/**/ + 3073, +/**/ + 3072, +/**/ + 3071, /**/ 3070, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index e3e1721f13..9569f2f4d3 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -274,7 +274,7 @@ arg_exists( if (len == 0) return FAIL; - for (idx = 0; idx < cctx->ctx_ufunc->uf_args.ga_len; ++idx) + for (idx = 0; idx < cctx->ctx_ufunc->uf_args_visible; ++idx) { char_u *arg = FUNCARG(cctx->ctx_ufunc, idx); @@ -6726,7 +6726,8 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) var_start = arg; for (var_idx = 0; var_idx == 0 || var_idx < var_count; var_idx++) { - int instr_count = -1; + int instr_count = -1; + int save_lnum; if (var_start[0] == '_' && !eval_isnamec(var_start[1])) { @@ -6979,13 +6980,20 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) goto theend; } + // Use the line number of the assignment for store instruction. + save_lnum = cctx->ctx_lnum; + cctx->ctx_lnum = start_lnum - 1; + if (lhs.lhs_has_index) { // Use the info in "lhs" to store the value at the index in the // list or dict. if (compile_assign_unlet(var_start, &lhs, TRUE, rhs_type, cctx) == FAIL) + { + cctx->ctx_lnum = save_lnum; goto theend; + } } else { @@ -7006,8 +7014,12 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx) generate_SETTYPE(cctx, lhs.lhs_type); if (generate_store_lhs(cctx, &lhs, instr_count) == FAIL) + { + cctx->ctx_lnum = save_lnum; goto theend; + } } + cctx->ctx_lnum = save_lnum; if (var_idx + 1 < var_count) var_start = skipwhite(lhs.lhs_dest_end + 1); @@ -7747,6 +7759,7 @@ compile_for(char_u *arg_start, cctx_T *cctx) type_T *vartype; type_T *item_type = &t_any; int idx; + int prev_lnum = cctx->ctx_prev_lnum; p = skip_var_list(arg_start, TRUE, &var_count, &semicolon, FALSE); if (p == NULL) @@ -7774,7 +7787,11 @@ compile_for(char_u *arg_start, cctx_T *cctx) if (cctx->ctx_compile_type == CT_DEBUG && instr->ga_len > 0 && ((isn_T *)instr->ga_data)[instr->ga_len - 1] .isn_type == ISN_DEBUG) + { --instr->ga_len; + prev_lnum = ((isn_T *)instr->ga_data)[instr->ga_len] + .isn_arg.debug.dbg_break_lnum; + } scope = new_scope(cctx, FOR_SCOPE); if (scope == NULL) @@ -7934,8 +7951,15 @@ compile_for(char_u *arg_start, cctx_T *cctx) } if (cctx->ctx_compile_type == CT_DEBUG) + { + int save_prev_lnum = cctx->ctx_prev_lnum; + // Add ISN_DEBUG here, so that the loop variables can be inspected. + // Use the prev_lnum from the ISN_DEBUG instruction removed above. + cctx->ctx_prev_lnum = prev_lnum; generate_instr_debug(cctx); + cctx->ctx_prev_lnum = save_prev_lnum; + } return arg_end; @@ -8397,10 +8421,17 @@ compile_finally(char_u *arg, cctx_T *cctx) this_instr = instr->ga_len; #ifdef FEAT_PROFILE if (cctx->ctx_compile_type == CT_PROFILE - && ((isn_T *)instr->ga_data)[instr->ga_len - 1] + && ((isn_T *)instr->ga_data)[this_instr - 1] .isn_type == ISN_PROF_START) + { // jump to the profile start of the "finally" --this_instr; + + // jump to the profile end above it + if (this_instr > 0 && ((isn_T *)instr->ga_data)[this_instr - 1] + .isn_type == ISN_PROF_END) + --this_instr; + } #endif // Fill in the "end" label in jumps at the end of the blocks. @@ -9153,7 +9184,6 @@ compile_def_function( { int count = ufunc->uf_def_args.ga_len; int first_def_arg = ufunc->uf_args.ga_len - count; - int uf_args_len = ufunc->uf_args.ga_len; int i; char_u *arg; int off = STACK_FRAME_SIZE + (ufunc->uf_va_name != NULL ? 1 : 0); @@ -9176,12 +9206,11 @@ compile_def_function( goto erret; // Make sure later arguments are not found. - ufunc->uf_args.ga_len = i; + ufunc->uf_args_visible = arg_idx; arg = ((char_u **)(ufunc->uf_def_args.ga_data))[i]; r = compile_expr0(&arg, &cctx); - ufunc->uf_args.ga_len = uf_args_len; if (r == FAIL) goto erret; @@ -9211,6 +9240,7 @@ compile_def_function( if (did_set_arg_type) set_function_type(ufunc); } + ufunc->uf_args_visible = ufunc->uf_args.ga_len; /* * Loop over all the lines of the function and generate instructions. diff --git a/src/vim9execute.c b/src/vim9execute.c index 3233e3ca3a..b194e6a2a2 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -26,6 +26,8 @@ typedef struct { int tcd_frame_idx; // ec_frame_idx at ISN_TRY int tcd_stack_len; // size of ectx.ec_stack at ISN_TRY + int tcd_in_catch; // in catch or finally block + int tcd_did_throw; // set did_throw in :endtry int tcd_catch_idx; // instruction of the first :catch or :finally int tcd_finally_idx; // instruction of the :finally block or zero int tcd_endtry_idx; // instruction of the :endtry @@ -82,7 +84,6 @@ struct ectx_S { funclocal_T ec_funclocal; garray_T ec_trystack; // stack of trycmd_T values - int ec_in_catch; // when TRUE in catch or finally block int ec_dfunc_idx; // current function index isn_T *ec_instr; // array with instructions @@ -1565,20 +1566,38 @@ exec_instructions(ectx_T *ectx) *msg_list = NULL; } - if (did_throw && !ectx->ec_in_catch) + if (did_throw) { garray_T *trystack = &ectx->ec_trystack; trycmd_T *trycmd = NULL; + int index = trystack->ga_len; // An exception jumps to the first catch, finally, or returns from // the current function. - if (trystack->ga_len > 0) - trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - 1; + while (index > 0) + { + trycmd = ((trycmd_T *)trystack->ga_data) + index - 1; + if (!trycmd->tcd_in_catch || trycmd->tcd_finally_idx != 0) + break; + // In the catch and finally block of this try we have to go up + // one level. + --index; + trycmd = NULL; + } if (trycmd != NULL && trycmd->tcd_frame_idx == ectx->ec_frame_idx) { - // jump to ":catch" or ":finally" - ectx->ec_in_catch = TRUE; - ectx->ec_iidx = trycmd->tcd_catch_idx; + if (trycmd->tcd_in_catch) + { + // exception inside ":catch", jump to ":finally" once + ectx->ec_iidx = trycmd->tcd_finally_idx; + trycmd->tcd_finally_idx = 0; + } + else + // jump to first ":catch" + ectx->ec_iidx = trycmd->tcd_catch_idx; + trycmd->tcd_in_catch = TRUE; + did_throw = FALSE; // don't come back here until :endtry + trycmd->tcd_did_throw = TRUE; } else { @@ -3202,6 +3221,7 @@ exec_instructions(ectx_T *ectx) trycmd_T *trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - 1; trycmd->tcd_caught = TRUE; + trycmd->tcd_did_throw = FALSE; } did_emsg = got_int = did_throw = FALSE; force_abort = need_rethrow = FALSE; @@ -3263,9 +3283,10 @@ exec_instructions(ectx_T *ectx) --trystack->ga_len; --trylevel; - ectx->ec_in_catch = FALSE; trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len; + if (trycmd->tcd_did_throw) + did_throw = TRUE; if (trycmd->tcd_caught && current_exception != NULL) { // discard the exception diff --git a/src/vim9type.c b/src/vim9type.c index 91dc3fe607..5cf1f42844 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -260,6 +260,7 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int do_member) type_T *type; type_T *member_type = &t_any; int argcount = 0; + int min_argcount = 0; if (tv->v_type == VAR_NUMBER) return &t_number; @@ -337,8 +338,7 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int do_member) if (idx >= 0) { - // TODO: get actual arg count and types - argcount = -1; + internal_func_get_argcount(idx, &argcount, &min_argcount); member_type = internal_func_ret_type(idx, 0, NULL); } else @@ -355,7 +355,20 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int do_member) if (ufunc->uf_func_type == NULL) set_function_type(ufunc); if (ufunc->uf_func_type != NULL) + { + if (tv->v_type == VAR_PARTIAL + && tv->vval.v_partial->pt_argc > 0) + { + type = get_type_ptr(type_gap); + if (type == NULL) + return NULL; + *type = *ufunc->uf_func_type; + type->tt_argcount -= tv->vval.v_partial->pt_argc; + type->tt_min_argcount -= tv->vval.v_partial->pt_argc; + return type; + } return ufunc->uf_func_type; + } } } @@ -364,6 +377,7 @@ typval2type_int(typval_T *tv, int copyID, garray_T *type_gap, int do_member) return NULL; type->tt_type = tv->v_type; type->tt_argcount = argcount; + type->tt_min_argcount = min_argcount; type->tt_member = member_type; return type; @@ -525,9 +539,10 @@ check_type(type_T *expected, type_T *actual, int give_msg, where_T where) ret = check_type(expected->tt_member, actual->tt_member, FALSE, where); if (ret == OK && expected->tt_argcount != -1 - && actual->tt_argcount != -1 - && (actual->tt_argcount < expected->tt_min_argcount - || actual->tt_argcount > expected->tt_argcount)) + && actual->tt_min_argcount != -1 + && (actual->tt_argcount == -1 + || (actual->tt_argcount < expected->tt_min_argcount + || actual->tt_argcount > expected->tt_argcount))) ret = FAIL; if (ret == OK && expected->tt_args != NULL && actual->tt_args != NULL) @@ -1032,7 +1047,10 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap) } } else + // Use -1 for "tt_argcount" to indicate an unknown number of + // arguments. *dest = alloc_func_type(common, -1, type_gap); + // Use the minimum of min_argcount. (*dest)->tt_min_argcount = type1->tt_min_argcount < type2->tt_min_argcount diff --git a/src/xxd/xxd.c b/src/xxd/xxd.c index 20f06ab7c5..94cc220bfb 100644 --- a/src/xxd/xxd.c +++ b/src/xxd/xxd.c @@ -55,16 +55,12 @@ * 11.01.2019 Add full 64/32 bit range to -o and output by Christer Jensen. * 04.02.2020 Add -d for decimal offsets by Aapo Rantalainen * - * (c) 1990-1998 by Juergen Weigert (jnweiger@informatik.uni-erlangen.de) + * (c) 1990-1998 by Juergen Weigert (jnweiger@gmail.com) * * I hereby grant permission to distribute and use xxd * under X11-MIT or GPL-2.0 (at the user's choice). * - * Small changes made afterwards by Bram Moolenaar et al. - * - * Distribute freely and credit me, - * make money and share with me, - * lose money and don't ask me. + * Contributions by Bram Moolenaar et al. */ /* Visual Studio 2005 has 'deprecated' many of the standard CRT functions */ @@ -135,7 +131,7 @@ extern void perror __P((char *)); extern long int strtol(); extern long int ftell(); -char version[] = "xxd V1.10 27oct98 by Juergen Weigert"; +char version[] = "xxd 2020-02-04 by Juergen Weigert et al."; #ifdef WIN32 char osver[] = " (Win32)"; #else