The Neovim Lua API provides several commands for creating user-commands, depending on whether the goal is to create a global command or a buffer-local command:.
The call signature for creating a global user-command is:
vim.api.nvim_create_user_command(
{name}, -- string
{command}, -- string or Lua function
{attributes} -- table
)
Similarly, buffer-local user commands can be created using:
vim.api.nvim_buf_create_user_command(
{buffer number}, -- integer
{name}, -- string
{command}, -- string or Lua function
{attributes} -- table
)
where the buffer number specifies the buffer to attach the command to, or 0 to attach to the current buffer
Other than the buffer number, these commands require the same positional arguments:
Name
User-commands must start with an upper-case letter. By convention camel-case is often used, though this is not required.
Command
The command can be a string containing Vim commands, or a Lua function that is called when the user command is invoked.
Command arguments
When the command is defined as a Lua function, each time the function is called a table of context information is passed as an argument to the function. The most important information is:
Key | Description |
---|---|
name | a string with the command name |
args | the string of arguments passed to the user command |
fargs | a list-like table containing each of the arguments passed to the command |
range | the number of items in the command range: 0, 1, or 2 |
line1 | the line number of the start of the range |
line2 | the line number of the end of the range |
count | any count supplied |
bang | true if the command was executed with a ! modifier, else false |
Attributes
When user-commands are created they are passed an attributes table that allows various aspects of the command to be configured. The most common attributes are:
nargs [int|string]
By default user-commands take no arguments. Allow command arguments by setting the nargs argument to one of:
nargs | Meaning |
---|---|
0 | No arguments are allowed (default) |
1 | Exactly 1 argument is required |
n | Exactly [n] arguments are required |
? | 0 or 1 arguments are allowed |
* | 0 or more arguments are allowed |
+ | 1 or more arguments are required |
When arguments are allowed, they should be passed after the command, with each argument separated by whitespace.
desc [str]
Set the desc attribute to a string that provides a brief description of the command.
bang [bool]
User-commands can accept a bang modifier to differentiate command behavior between:
:Command
and
:Command!
Enable bang detection by setting the bang attribute to true. When bang is true, the bang attribute will indicate whether or not the bang modifier was used when the command was invoked.
As a simple example, consider the following user-command:
local callback = function(args)
local lines = {}
-- detect if the user includes a bang
if args.bang == true then
table.insert(lines, "bang was used")
else
table.insert(lines, "bang was not used")
end
vim.api.nvim_buf_set_lines(0, 0, -1, true, lines)
end
vim.api.nvim_create_user_command("Bang", callback, {
nargs = 0,
desc = "User Command with bang",
-- enable bang detection
bang = true,
})
which detects whether or not the bang modifier is present in the command invocation, and updates the buffer text accordingly.
Here is the result When the command is called without a bang modifier:
For comparison, here is the result when the call includes a bang modifier:
complete [string|function]
When a command expects one or more arguments, argument completion can be enabled by setting the completion to one of the available values. The following table provides a sample of the completion providers:
Value | Description |
---|---|
arglist | file names from the argument list |
augroup | autocmd groups |
buffer | buffer names |
color | color schemes |
command | Ex commands and arguments |
dir | directory names |
environment | environment variable names |
event | autocommand events |
file | file and directory names |
file_in_path | file and directory names in 'path' |
filetype | filetype names |
highlight | highlight groups |
lua | Lua expression |
mapping | mapping name |
shellcmd | Shell command |
var | user variables |
Neovim additionally supports custom completion functions, by setting the value for the complete key to a Lua function, as follows:
vim.api.nvim_create_user_command("Command", callback, {
nargs = 1,
desc = "User Command with completion",
complete = function(ArgLead, CmdLine, CursorPos)
return { "one", "two", "three" }
end,
})
The arguments passed to the Lua function are:
Argument | Description |
---|---|
ArgLead | the leading portion of the argument currently being completed on |
CmdLine | the entire command line |
CursorPos | the position of the cursor |
The function should return a list-like table containing all completion candidates. Note that the complete function does not need to implement filtering; Neovim will automatically filter the returned list.
count [bool]
By default, user-defined commands do not accept a count. Set the count attribute to true to support optional counts.
When count is true, a count may be passed to the user-function in either of two ways:
:[count]Command
:Command [count]
Count information is passed to the Lua function as an integer value associated with the count key.
As a simple example, consider the following user-command:
local callback = function(args)
local lines = {}
for i = 0, args.count do
table.insert(lines, tostring(i))
end
vim.api.nvim_buf_set_lines(0, 0, -1, true, lines)
end
vim.api.nvim_create_user_command("Count", callback, {
nargs = 0,
desc = "User Command with count",
-- enable count detection
count = true,
})
This command takes a count, then replaces the buffer contents with lines that count up to the specified number. As a first example the count is passed as an argument to the command:
As a second example, the count is passed as a prefix to the command:
Note that because passing the count as a prefix is identical to passing a single-list range, count and range are mutually-exclusive; user commands can accept one, the other, or neither.
range [bool]
By default, user-defined commands do not accept a range. Set the range attribute to true to support optional ranges. As mentioned in the count section, count and range are mutually-exclusive.
When range is true, information about the range is passed to the Lua function using a combination of the range, line1, and line2 keys. range will be assigned an integer value of 0, 1, or 2, indicating the type of range that was specified:
Value | range | line1 | line2 |
---|---|---|---|
0 | None | current line | current line |
1 | Single Line | line index | line index |
2 | Multi-line | range start | range end |
As a simple example, consider the following user-command:
local decorate = function(lines, range_start, range_end, text)
for i = range_start, range_end do
lines[i] = lines[i] .. " " .. text
end
return lines
end
local callback = function(args)
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, true)
if args.range == 0 then
-- no range given
-- decorate all lines (per Vim standard behavior)
lines = decorate(lines, 1, #lines, args.range)
else
-- single or multi-line range
lines = decorate(lines, args.line1, args.line2, args.range)
end
vim.api.nvim_buf_set_lines(0, 0, -1, true, lines)
end
vim.api.nvim_create_user_command("Range", callback, {
nargs = 0,
desc = "User Command with Range",
-- enable range detection
range = true,
})
This command takes an optional range, then:
-
if no range is specified, add " 0" to each line in the buffer,
-
if a single-line range is specified, add " 1" to the specified line,
-
if a multi-line range is specified, then add " 2" to each line in the specified range.
We will execute this command using the following buffer:
First, although ranges are supported they are not required. This command detects this condition and decorates the entire buffer:
Next, we specify a single-line range:
Finally, we specify a multi-line range:
As with any range specifications, we could have used visual selection, shortcuts such as %
,
.
, etc.