51

I want to make a function that has optional arguments.

In python I would do something like this:

def functionName(arg1,arg2=false,arg3=false):
    if arg2:
        #Do stuff with arguments
    else:
        #Do stuff without arguments

I have tried doing function FunctionName(arg1,arg2=false,arg3=false), but it just gives me a error saying invalid argument.

How would I do this in vimscript?

6 Answers 6

53

Yes, you can take optional arguments in a function. It is not as convenient as python's way of doing it, but this is how you do it:

function FooBar(...) " This is like *args in python
    echom a:0 " a:0 contains an integer which is the number of arguments passed to the function
    echom a:1 " a:1 contains the first argument passed, a:2 contains the second and so on
    echo a:000 " a:000 contains a list of all arguments that were passed to the function
endfunction

Note that you may only have up to 20 arguments in this fashion.

Relevant help topics:

:help :function
:help function-argument
0
53

Late to the party a bit but I didn't see my favorite one:

function! FunctionName(arg1,...)
    let arg2 = get(a:, 1, 0)
    let arg3 = get(a:, 2, 0)

    if arg2
        "Do stuff with arguments"
    else
        "Do stuff without arguments"
    endif
endfunction

in which get(a:, n, default) will get the nth optional argument returning default if it's not present.

4
  • Oooh that's nice! Commented Sep 6, 2017 at 1:57
  • 6
    I know this is super old, but just FYI to anyone reading this. You cannot do let a:arg= anymore in vim. You must use something like let l:arg= or just let arg=. See github.com/vim/vim/commit/…
    – jmzagorski
    Commented Feb 21, 2019 at 18:37
  • Where is a: documented? Is it the same as a:000?
    – user202729
    Commented Apr 8, 2020 at 6:46
  • 1
    @user202729 Check out :help internal-variables, there is a bit explaining that scopes like "a:" or "s:" can be used as dictionaries. It's not quite the same as a:000 because that's a list, as such it's zero-indexed and you would need to use get(a:000, n-1, default) instead, but other than that you can use it no problem. (reposted to fix mistake)
    – phicr
    Commented May 7, 2021 at 18:12
28

This pattern will let you assign meaningful names to each argument, and provide a default value for any arguments that were not provided:

function FunctionName(foo, ...)
  let bar = a:0 >= 1 ? a:1 : 0
  let baz = a:0 >= 2 ? a:2 : 0
  ...
  " Code that makes use of a:foo, bar and baz

As pointed out by Boris Brodski:

a:0 counts the number of optional arguments passed

a:1, a:2, ... let us access the optional arguments

The mandatory arguments (just foo in the example above) are not counted

condition ? result_if_true : result_if_false is the conditional (ternary) expression, which evaluates to the second or third term depending on whether the first term was true or not.

So if no third argument is provided, baz will take the default value of 0.

One concern with the above example is that a:foo can only be accessed with the a: prefix, whilst bar and baz can do without a prefix. Since this is not very consistent, you may prefer to pull out all of the arguments into local variables, like so:

function FunctionName(...)
  let foo = a:1                  " Will throw an error if no arg was provided
  let bar = a:0 >= 2 ? a:2 : 0
  let baz = a:0 >= 3 ? a:3 : 0
  ...
  " Code that makes use of foo, bar and baz

(Technically, you can use the l: prefix to refer to local variables inside a function, for example l:baz, but this is redundant so I would not recommend it.)


But I do recommend that you use this form whenever possible:

function! s:FunctionName(...)

The ! allows you to redefine your function at runtime (e.g. by reloading the script), and the s: limits the function to script scope. That avoids polluting the global namespace (and risking collision) if your function is only referenced from elsewhere inside the script. It is generally the preferred way to define functions when they do not need to be globally visible. ;-)

2
  • 1
    a:0 doesn't include the fixed named parameters. a:1 is the first parameter within the ... (Just tested with VIM 8.0) Commented Apr 30, 2018 at 11:54
  • Thanks @BorisBrodski you're right, good catch. I have corrected the answer. Commented May 1, 2018 at 14:44
19

Since Vim 8.1.1310 Vim also supports real optional function arguments.

However, that means that most vim installation don't support this yet. Neovim has that feature since version 0.7.0.

Example from :help optional-function-argument:

  function Something(key, value = 10)
     echo a:key .. ": " .. a:value
  endfunction
  call Something('empty')   "empfty: 10"
  call Something('key', 20) "key: 20"   
2
  • 1
    Neat. You should expand your answer to give an example so we can grasp quickly what it adds. Commented Nov 21, 2019 at 10:53
  • 1
    @LucHermitte I modified my answer accordingly.
    – radlan
    Commented Nov 22, 2019 at 8:52
8

I'm not well versed in vimscript, but this is how I would do it:

function Func(foo, ...)
  if a:0 < 1
    let bar = "Unspecified bar"
  else
    let bar = a:1
  endif
  if a:0 < 2
    let baz = "Unspecified baz"
  else
    let baz = a:2
  endif

  echo a:foo
  echo bar
  echo baz
endfunction

You can then call Func("Hello", "World"), and see that it prints "Hello", "World", "Unspecified baz".

This is using the varargs feature of vim functions, which lets you pass an arbitrary number of extra arguments to the function. The arguments are stored in variables which, depending on a:0 (the length of the extra arguments list), either get set to their corresponding argument or a default value.

0
1

If you want named parameter passing in any programming language that doesn't support it, pass a dictionary instead:

function! TestOptions(options = {})
  let foo = get(a:options, 'foo', '')
  let bar = get(a:options, 'bar', 'default_bar')
  if l:foo != ''
    echom "Picked foo " . l:foo
  endif
endfunction

In this case, foo is an option that we check with an if so that we can modify the function behaviour, while bar is just silently using "default_bar" if unspecified. For example, calling with call TestOptions({'foo': 'something'}) invokes the echo.

This solves the problem of multiple optional parameters where you need to know which one was passed, and is used extensively in fzf.vim's interface.

1
  • 1
    Don't forget the literal dict syntax, too (#{foo: 'something'})
    – D. Ben Knoble
    Commented Jul 6, 2023 at 13:50

Not the answer you're looking for? Browse other questions tagged or ask your own question.