Skip to content

Commit

Permalink
patch 8.2.2744: Vim9: no way to explicitly ignore an argument
Browse files Browse the repository at this point in the history
Problem:    Vim9: no way to explicitly ignore an argument.
Solution:   Use the underscore as the name for an ignored argument.
  • Loading branch information
brammool committed Apr 10, 2021
1 parent 599410c commit 962c43b
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 5 deletions.
29 changes: 25 additions & 4 deletions runtime/doc/vim9.txt
Expand Up @@ -137,18 +137,21 @@ arguments).
Vim9 functions ~

A function defined with `:def` is compiled. Execution is many times faster,
often 10x to 100x times.
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 either of these is encountered:
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 where the
- 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
Expand All @@ -161,7 +164,7 @@ functions.

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<number>)
Expand All @@ -176,6 +179,15 @@ should use its default value. Example: >
...
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

This comment has been minimized.

Copy link
@lacygoill

lacygoill Apr 10, 2021

Line 189 in doc/vim9.txt:

There is no error for using the "_" argument multiple times.

It doesn't work in a lambda passed to map():

vim9script
echo range(3)->map((_, _) => 123)
E853: Duplicate argument name: _

Tested on 8.2.2744.

This comment has been minimized.

Copy link
@lacygoill

lacygoill Apr 10, 2021

Fixed by 8.2.2745.

be given.


Functions and variables are script-local by default ~
Expand Down Expand Up @@ -433,6 +445,15 @@ But you can use a backslash to concatenate the lines before parsing: >
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) => {
Expand Down
2 changes: 2 additions & 0 deletions src/errors.h
Expand Up @@ -397,3 +397,5 @@ EXTERN char e_failed_to_extract_pwd_from_str_check_your_shell_config[]
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"));
7 changes: 6 additions & 1 deletion src/eval.c
Expand Up @@ -3514,7 +3514,12 @@ eval7(
{
int flags = evalarg == NULL ? 0 : evalarg->eval_flags;

if ((in_vim9script() ? **arg : *skipwhite(*arg)) == '(')
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);
Expand Down
5 changes: 5 additions & 0 deletions src/evalvars.c
Expand Up @@ -3188,6 +3188,11 @@ set_var_const(
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);

Expand Down
35 changes: 35 additions & 0 deletions src/testdir/test_vim9_func.vim
Expand Up @@ -2619,6 +2619,41 @@ def Test_compile_error()
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))

This comment has been minimized.

Copy link
@lacygoill

lacygoill Apr 10, 2021

Line 2628 in testdir/test_vim9_func.vim.

This test doesn't pass, and the code doesn't work in a standalone script:

vim9script
def Ignore(_, _): string
    return 'yes'
enddef
E1077: Missing argument type for _

Tested on 8.2.2744.

This comment has been minimized.

Copy link
@lacygoill

lacygoill Apr 10, 2021

Fixed by 8.2.2745.


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
2 changes: 2 additions & 0 deletions src/version.c
Expand Up @@ -750,6 +750,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2744,
/**/
2743,
/**/
Expand Down
11 changes: 11 additions & 0 deletions src/vim9compile.c
Expand Up @@ -4416,6 +4416,12 @@ compile_expr7(

// "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);
Expand Down Expand Up @@ -6378,6 +6384,11 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
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)
{
Expand Down

1 comment on commit 962c43b

@lacygoill
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If Vim is more strict regarding the arguments specified in a lambda, shouldn't this snippet raise an error?

vim9script
echo [0, 1, 2]->map(() => 123)
[123, 123, 123]

It does not, even though:

  • the lambda is passed the index of each item in the list, as well as the item itself
  • we didn't specify any argument in the lambda

Shouldn't we be forced to explicitly tell Vim about those arguments?

echo [0, 1, 2]->map((_, v) => 123)
                     ^--^

Just like we now have to explicitly tell Vim that a timer's callback is expected to receive its id as an argument:

vim9script
timer_start(0, () => 123)
E118: Too many arguments for function: <lambda>1
vim9script
timer_start(0, (_) => 123)
no error

Please sign in to comment.