52

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

55

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
55

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/… Commented Feb 21, 2019 at 18:37
  • Where is a: documented? Is it the same as a:000? 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) Commented May 7, 2021 at 18:12
29

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
21

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. 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'}) Commented Jul 6, 2023 at 13:50

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.