Let's start with `vim.api.nvim_add_user_command()`
The first argument passed to this function is the name of the command (which must start with an uppercase letter).
The second argument is the code to execute when invoking said command. It can either be:
A string (in which case it will be executed as Vimscript). You can use escape sequences like `<q-args>`, `<range>`, etc. like you would with `:command`
vim.cmd('Upper hello world') -- prints "HELLO WORLD"
```
Or a Lua function. It receives a dictionary-like table that contains the data normally provided by escape sequences (see [`:help nvim_add_user_command()`](https://neovim.io/doc/user/api.html#nvim_add_user_command()) for a list of available keys)
```lua
vim.api.nvim_add_user_command(
'Upper',
function(opts)
print(string.upper(opts.args))
end,
{ nargs = 1 }
)
```
The third argument lets you pass command attributes as a table (see [`:help command-attributes`](https://neovim.io/doc/user/map.html#command-attributes)). Since you can already define buffer-local user commands with `vim.api.nvim_buf_add_user_command()`, `-buffer` is not a valid attribute.
Two additional attributes are available:
- `desc` allows you to control what gets displayed when you run `:command {cmd}` on a command defined as a Lua callback.
- `force` is equivalent to calling `:command!` and replaces a command if one with the same name already exists. It is true by default, unlike its Vimscript equivalent.
The `-complete` attribute can take a Lua function in addition to the attributes listed in [`:help :command-complete`](https://neovim.io/doc/user/map.html#:command-complete).
-- return completion candidates as a list-like table
return { 'foo', 'bar', 'baz' }
end,
})
```
Buffer-local user commands also take a buffer number as their first argument. This is an advantage over `-buffer` which can only define a command for the current buffer.
The `<args>` and `<f-args>` escape sequences are not available when using a Lua function, the `args` key is always a string containing the arguments passed to the command. If you need to get each argument separately, the string has to be tokenized manually. Keep in mind that the behavior of `<f-args>` is subtly different depending on the `-nargs` attribute.
```vim
command! -nargs=1 Test1 echo [<f-args>]
command! -nargs=* Test2 echo [<f-args>]
Test1 this is a\ test
" prints `['this is a\ test']`
Test2 this is a\ test
" prints `['this', 'is', 'a test']`
```
The `:Test1` command prints what was typed verbatim. `:Test2` separates each word and gets rid of whitespace except when preceded by a backslash `\`.
When using a Lua function, the `nargs` attribute does not change the value of `args`:
The `-complete=custom` attribute automatically filters completion candidates and has built-in wildcard ([`:help wildcard`](https://neovim.io/doc/user/editing.html#wildcard)) support: