To: vim_dev@googlegroups.com Subject: Patch 8.2.2744 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2744 Problem: Vim9: no way to explicitly ignore an argument. Solution: Use the underscore as the name for an ignored argument. Files: runtime/doc/vim9.txt, src/vim9compile.c, src/eval.c, src/evalvars.c, src/errors.h, src/testdir/test_vim9_func.vim *** ../vim-8.2.2743/runtime/doc/vim9.txt 2021-03-29 22:14:45.604788424 +0200 --- runtime/doc/vim9.txt 2021-04-10 16:12:44.885809613 +0200 *************** *** 137,154 **** Vim9 functions ~ A function defined with `:def` is compiled. Execution is many times faster, ! often 10x to 100x times. Many errors are already found when compiling, before the function is executed. The syntax is strict, to enforce code that is easy to read and understand. ! Compilation is done when either of these is encountered: - the first time the function is called ! - when the `:defcompile` command is encountered in the script where the function was defined - `:disassemble` is used for the function. - a function that is compiled calls the function or uses it as a function reference `:def` has no options like `:function` does: "range", "abort", "dict" or "closure". A `:def` function always aborts on an error (unless `:silent!` was --- 137,157 ---- Vim9 functions ~ A function defined with `:def` is compiled. Execution is many times faster, ! often 10 to 100 times. Many errors are already found when compiling, before the function is executed. The syntax is strict, to enforce code that is easy to read and understand. ! Compilation is done when any of these is encountered: - the first time the function is called ! - when the `:defcompile` command is encountered in the script after the function was defined - `:disassemble` is used for the function. - a function that is compiled calls the function or uses it as a function reference + *E1091* + If compilation fails it is not tried again on the next call, instead this + error is given: "E1091: Function is not compiled: {name}". `:def` has no options like `:function` does: "range", "abort", "dict" or "closure". A `:def` function always aborts on an error (unless `:silent!` was *************** *** 161,167 **** Arguments are accessed by name, without "a:", just like any other language. There is no "a:" dictionary or "a:000" list. ! Variable arguments are defined as the last argument, with a name and have a list type, similar to TypeScript. For example, a list of numbers: > def MyFunc(...itemlist: list) --- 164,170 ---- Arguments are accessed by name, without "a:", just like any other language. There is no "a:" dictionary or "a:000" list. ! *vim9-variable-arguments* Variable arguments are defined as the last argument, with a name and have a list type, similar to TypeScript. For example, a list of numbers: > def MyFunc(...itemlist: list) *************** *** 176,181 **** --- 179,193 ---- ... enddef MyFunc(v:none, 'LAST') # first argument uses default value 'one' + < + *vim9-ignored-argument* + The argument "_" (an underscore) can be used to ignore the argument. This is + most useful in callbacks where you don't need it, but do need to give an + argument to match the call. E.g. when using map() two arguments are passed, + the key and the value, to ignore the key: > + map(myList, (_, v) => v * 2) + There is no error for using the "_" argument multiple times. No type needs to + be given. Functions and variables are script-local by default ~ *************** *** 204,210 **** for clarity. Since a script-local function reference can be used without "s:" the name must ! start with an upper case letter even when using the ":s" prefix. In legacy script "s:funcref" could be used, because it could not be referred to with "funcref". In Vim9 script it can, therefore "s:Funcref" must be used to avoid that the name interferes with builtin functions. --- 216,222 ---- for clarity. Since a script-local function reference can be used without "s:" the name must ! start with an upper case letter even when using the "s:" prefix. In legacy script "s:funcref" could be used, because it could not be referred to with "funcref". In Vim9 script it can, therefore "s:Funcref" must be used to avoid that the name interferes with builtin functions. *************** *** 305,317 **** or imported variables and functions in the same script file. Variables may shadow Ex commands, rename the variable if needed. ! Global variables and user defined functions must be prefixed with "g:", also ! at the script level. > vim9script var script_local = 'text' g:global = 'value' var Funcref = g:ThatFunction Since `&opt = value` is now assigning a value to option "opt", ":&" cannot be used to repeat a `:substitute` command. --- 317,337 ---- or imported variables and functions in the same script file. Variables may shadow Ex commands, rename the variable if needed. ! Global variables must be prefixed with "g:", also at the script level. > vim9script var script_local = 'text' g:global = 'value' var Funcref = g:ThatFunction + Global functions must be prefixed with "g:" when defining them, but can be + called without "g:". > + vim9script + def g:GlobalFunc(): string + return 'text' + enddef + echo GlobalFunc() + The "g:" prefix is not needed for auto-load functions. + Since `&opt = value` is now assigning a value to option "opt", ":&" cannot be used to repeat a `:substitute` command. *************** *** 401,407 **** Lambda using => instead of -> ~ ! In legacy script there can be confusion between using "->" for a method call and for a lambda. Also, when a "{" is found the parser needs to figure out if it is the start of a lambda or a dictionary, which is now more complicated --- 421,427 ---- Lambda using => instead of -> ~ ! *vim9-lambda* In legacy script there can be confusion between using "->" for a method call and for a lambda. Also, when a "{" is found the parser needs to figure out if it is the start of a lambda or a dictionary, which is now more complicated *************** *** 425,436 **** --- 445,472 ---- filter(list, (k, \ v) \ => v > 0) + < *vim9-lambda-arguments* + In legacy script a lambda could be called with any number of extra arguments, + there was no way to warn for not using them. In Vim9 script the number of + arguments must match. If you do want to accept any arguments, or any further + arguments, use "..._", which makes the function accept + |vim9-variable-arguments|. Example: > + var Callback = (..._) => 'anything' + echo Callback(1, 2, 3) # displays "anything" + < *inline-function* Additionally, a lambda can contain statements in {}: > var Lambda = (arg) => { g:was_called = 'yes' return expression } + This can be useful for a timer, for example: > + var count = 0 + var timer = timer_start(500, (_) => { + count += 1 + echom 'Handler called ' .. count + }, {repeat: 3}) + The ending "}" must be at the start of a line. It can be followed by other characters, e.g.: > *************** *** 836,842 **** The 'edcompatible' option value is not used. The 'gdefault' option value is not used. ! You may also find this wiki useful. It was written by an early adoptor of Vim9 script: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md ============================================================================== --- 872,878 ---- The 'edcompatible' option value is not used. The 'gdefault' option value is not used. ! You may also find this wiki useful. It was written by an early adopter of Vim9 script: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md ============================================================================== *************** *** 881,894 **** :enddef End of a function defined with `:def`. It should be on a line by its own. ! You may also find this wiki useful. It was written by an early adoptor of Vim9 script: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md If the script the function is defined in is Vim9 script, then script-local variables can be accessed without the "s:" prefix. They must be defined before the function is compiled. If the script the function is defined in is legacy script, then script-local variables must be accessed with the "s:" ! prefix and they do not need to exist (they can be deleted any time). *:defc* *:defcompile* :defc[ompile] Compile functions defined in the current script that --- 917,930 ---- :enddef End of a function defined with `:def`. It should be on a line by its own. ! You may also find this wiki useful. It was written by an early adopter of Vim9 script: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md If the script the function is defined in is Vim9 script, then script-local variables can be accessed without the "s:" prefix. They must be defined before the function is compiled. If the script the function is defined in is legacy script, then script-local variables must be accessed with the "s:" ! prefix if they do not exist at the time of compiling. *:defc* *:defcompile* :defc[ompile] Compile functions defined in the current script that *************** *** 1073,1084 **** ['a', 'b', 'c'] list [1, 'x', 3] list Stricter type checking ~ *type-checking* In legacy Vim script, where a number was expected, a string would be automatically converted to a number. This was convenient for an actual number ! such as "123", but leads to unexpected problems (but no error message) if the string doesn't start with a number. Quite often this leads to hard-to-find bugs. --- 1109,1123 ---- ['a', 'b', 'c'] list [1, 'x', 3] list + For script-local variables in Vim9 script the type is checked, also when the + variable was declared in a legacy function. + Stricter type checking ~ *type-checking* In legacy Vim script, where a number was expected, a string would be automatically converted to a number. This was convenient for an actual number ! such as "123", but leads to unexpected problems (and no error message) if the string doesn't start with a number. Quite often this leads to hard-to-find bugs. *** ../vim-8.2.2743/src/vim9compile.c 2021-04-10 14:03:40.312675756 +0200 --- src/vim9compile.c 2021-04-10 16:58:01.179636601 +0200 *************** *** 4416,4421 **** --- 4416,4427 ---- // "name" or "name()" p = to_name_end(*arg, TRUE); + if (p - *arg == (size_t)1 && **arg == '_') + { + emsg(_(e_cannot_use_underscore_here)); + return FAIL; + } + if (*p == '(') { r = compile_call(arg, p - *arg, cctx, ppconst, 0); *************** *** 6378,6383 **** --- 6384,6394 ---- semsg(_(e_cannot_assign_to_constant), lhs.lhs_name); goto theend; } + if (is_decl && lhs.lhs_name[0] == '_' && lhs.lhs_name[1] == NUL) + { + emsg(_(e_cannot_use_underscore_here)); + goto theend; + } if (!heredoc) { *** ../vim-8.2.2743/src/eval.c 2021-04-05 20:50:56.548776807 +0200 --- src/eval.c 2021-04-10 17:09:31.357077770 +0200 *************** *** 3514,3520 **** { int flags = evalarg == NULL ? 0 : evalarg->eval_flags; ! if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(') { // "name(..." recursive! *arg = skipwhite(*arg); --- 3514,3525 ---- { int flags = evalarg == NULL ? 0 : evalarg->eval_flags; ! if (in_vim9script() && len == 1 && *s == '_') ! { ! emsg(_(e_cannot_use_underscore_here)); ! ret = FAIL; ! } ! else if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(') { // "name(..." recursive! *arg = skipwhite(*arg); *** ../vim-8.2.2743/src/evalvars.c 2021-03-31 21:47:30.255015197 +0200 --- src/evalvars.c 2021-04-10 17:09:20.321117361 +0200 *************** *** 3188,3193 **** --- 3188,3198 ---- goto failed; } var_in_vim9script = is_script_local && current_script_is_vim9(); + if (var_in_vim9script && name[0] == '_' && name[1] == NUL) + { + emsg(_(e_cannot_use_underscore_here)); + goto failed; + } di = find_var_in_ht(ht, 0, varname, TRUE); *** ../vim-8.2.2743/src/errors.h 2021-04-09 20:22:57.843301002 +0200 --- src/errors.h 2021-04-10 16:19:04.461115256 +0200 *************** *** 397,399 **** --- 397,401 ---- INIT(= N_("E1179: Failed to extract PWD from %s, check your shell's config related to OSC 7")); EXTERN char e_variable_arguments_type_must_be_list_str[] INIT(= N_("E1180: Variable arguments type must be a list: %s")); + EXTERN char e_cannot_use_underscore_here[] + INIT(= N_("E1181: Cannot use an underscore here")); *** ../vim-8.2.2743/src/testdir/test_vim9_func.vim 2021-04-10 14:03:40.312675756 +0200 --- src/testdir/test_vim9_func.vim 2021-04-10 17:14:41.524338799 +0200 *************** *** 2619,2624 **** --- 2619,2659 ---- delfunc g:Broken enddef + def Test_ignored_argument() + var lines =<< trim END + vim9script + def Ignore(_, _): string + return 'yes' + enddef + assert_equal('yes', Ignore(1, 2)) + + func Ok(_) + return a:_ + endfunc + assert_equal('ok', Ok('ok')) + + func Oktoo() + let _ = 'too' + return _ + endfunc + assert_equal('too', Oktoo()) + END + CheckScriptSuccess(lines) + + lines =<< trim END + def Ignore(_: string): string + return _ + enddef + defcompile + END + CheckScriptFailure(lines, 'E1181:', 1) + + lines =<< trim END + var _ = 1 + END + CheckDefAndScriptFailure(lines, 'E1181:', 1) + enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker *** ../vim-8.2.2743/src/version.c 2021-04-10 14:03:40.312675756 +0200 --- src/version.c 2021-04-10 16:16:26.085438831 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2744, /**/ -- From "know your smileys": :q vi user saying, "How do I get out of this damn emacs editor?" /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///