The vim module is Neovim’s Lua “standard library” - a set of core functions and variables that are always available.
It provides access to Vimscript functions, variables, commands, and options through a Lua interface.
Module Overview
The vim module is automatically loaded - no require("vim") needed.
-- Inspect what's available
vim.print(vim)
Core Functions
vim.cmd()
Executes Vimscript commands from Lua.
Command(s) to execute. String form supports multiline Vimscript.
-- Single command
vim.cmd('echo "Hello, Nvim!"')
-- Multiline script
vim.cmd([[
augroup my_group
autocmd!
autocmd FileType lua setlocal shiftwidth=2
augroup END
]])
-- Table form (structured)
vim.cmd { cmd = 'write', args = { 'myfile.txt' }, bang = true }
-- Indexed form
vim.cmd.echo('"foo"')
vim.cmd.write { 'myfile.txt', bang = true }
vim.print()
Pretty-prints Lua values with syntax highlighting.
local config = { timeout = 1000, features = { 'lsp', 'treesitter' } }
vim.print(config)
-- {
-- features = { "lsp", "treesitter" },
-- timeout = 1000
-- }
-- Chain with other operations
local hl = vim.print(vim.api.nvim_get_hl(0, { name = 'Normal' }))
vim.inspect()
Converts any Lua value to a human-readable string.
local data = { a = 1, b = { c = 2 } }
local str = vim.inspect(data)
print(str) -- "{ a = 1, b = { c = 2 } }"
vim.schedule()
Defers function execution to the main event loop.
Function to schedule for execution
Returns nil on success, error message on failure
vim.schedule(function()
-- Safe to call API functions here
vim.api.nvim_command('echom "Scheduled!"')
end)
Use vim.schedule() to safely call API functions from callbacks like timers or libuv events.
vim.schedule_wrap()
Returns a wrapped version of a function that will be called via vim.schedule().
local timer = vim.uv.new_timer()
timer:start(1000, 0, vim.schedule_wrap(function()
vim.api.nvim_command('echom "Timer fired!"')
end))
Table Utilities
vim.deepcopy()
Creates a deep copy of a table.
When true, tables are never reused (no cycle detection). Default: false
local original = { a = 1, nested = { b = 2 } }
local copy = vim.deepcopy(original)
copy.nested.b = 3
vim.print(original.nested.b) -- 2 (unchanged)
vim.print(copy.nested.b) -- 3
vim.tbl_extend()
Merges multiple tables.
local defaults = { timeout = 1000, retries = 3 }
local user_config = { timeout = 2000 }
local config = vim.tbl_extend('force', defaults, user_config)
vim.print(config) -- { timeout = 2000, retries = 3 }
-- Last value wins
local result = vim.tbl_extend('force', { a = 1 }, { a = 2 })
-- result = { a = 2 }
vim.tbl_deep_extend()
Recursively merges tables.
local base = { ui = { width = 80, height = 24 } }
local overrides = { ui = { width = 120 } }
local config = vim.tbl_deep_extend('force', base, overrides)
vim.print(config)
-- { ui = { width = 120, height = 24 } }
vim.tbl_keys() / vim.tbl_values()
local t = { a = 1, b = 2, c = 3 }
vim.print(vim.tbl_keys(t)) -- { "a", "b", "c" }
vim.print(vim.tbl_values(t)) -- { 1, 2, 3 }
vim.tbl_map() / vim.tbl_filter()
-- Map: transform values
local doubled = vim.tbl_map(function(v) return v * 2 end, { 1, 2, 3 })
vim.print(doubled) -- { 2, 4, 6 }
-- Filter: keep only matching values
local evens = vim.tbl_filter(function(v) return v % 2 == 0 end, { 1, 2, 3, 4 })
vim.print(evens) -- { 2, 4 }
vim.tbl_contains() / vim.list_contains()
-- Check if table contains value
vim.tbl_contains({ 1, 2, 3 }, 2) -- true
vim.list_contains({ 'a', 'b' }, 'c') -- false
-- With predicate
vim.tbl_contains({ { id = 1 }, { id = 2 } }, function(v)
return v.id == 2
end, { predicate = true }) -- true
vim.tbl_isempty() / vim.islist() / vim.isarray()
vim.tbl_isempty({}) -- true
vim.tbl_isempty({ a = 1 }) -- false
vim.islist({ 1, 2, 3 }) -- true (contiguous integers from 1)
vim.islist({ [1] = 'a', [3] = 'c' }) -- false (has gap)
vim.isarray({ [1] = 'a', [5] = 'b' }) -- true (integer keys)
vim.isarray({ a = 1 }) -- false (string key)
String Utilities
vim.split() / vim.gsplit()
-- Eager split (returns table)
local parts = vim.split('a:b:c', ':')
vim.print(parts) -- { "a", "b", "c" }
-- Lazy split (returns iterator)
for part in vim.gsplit('a:b:c', ':') do
print(part)
end
-- With options
vim.split('a::b:', ':', { trimempty = true }) -- { "a", "b" }
vim.split('a.b.c', '.', { plain = true }) -- { "a", "b", "c" }
vim.trim() / vim.startswith() / vim.endswith()
vim.trim(' hello ') -- "hello"
vim.startswith('hello', 'hel') -- true
vim.endswith('world', 'ld') -- true
Validation
vim.validate()
Validates function arguments with helpful error messages.
function my_function(name, age)
vim.validate('name', name, 'string')
vim.validate('age', age, 'number')
-- Function body
end
my_function('Alice', 25) -- OK
my_function(123, 25) -- Error: name: expected string, got number
-- Multiple types allowed
vim.validate('id', value, { 'number', 'string' })
-- Custom validator
vim.validate('port', 8080, function(v)
return v > 0 and v < 65536
end, 'valid port number')
-- Optional parameters
vim.validate('config', nil, 'table', true) -- OK (optional)
Special Values
vim.NIL
Represents null in RPC/JSON contexts.
local t = { key = vim.NIL }
-- Converts to JSON: {"key": null}
vim.empty_dict()
Creates an explicitly empty dictionary (vs. empty array).
local empty_dict = vim.empty_dict()
local empty_array = {}
-- When passed to Vimscript:
-- empty_dict -> {}
-- empty_array -> []
Utility Functions
vim.notify()
Displays notifications to the user.
vim.notify('Operation completed', vim.log.levels.INFO)
vim.notify('Warning: file modified', vim.log.levels.WARN)
vim.notify('Error occurred', vim.log.levels.ERROR)
-- With options
vim.notify('Custom notification', vim.log.levels.INFO, {
title = 'My Plugin'
})
vim.defer_fn()
Executes a function after a delay.
vim.defer_fn(function()
print('Executed after 1 second')
end, 1000)
vim.wait()
Waits for a condition or timeout.
-- Wait up to 5000ms for condition
local ok = vim.wait(5000, function()
return vim.g.ready == true
end, 100) -- check every 100ms
if ok then
print('Condition met')
else
print('Timed out')
end
Type Checking
vim.is_callable()
vim.is_callable(function() end) -- true
vim.is_callable(print) -- true
vim.is_callable('string') -- false
-- Works with __call metamethod
local t = setmetatable({}, { __call = function() end })
vim.is_callable(t) -- true
Miscellaneous
vim.log.levels
Log level constants for notifications.
vim.log.levels.TRACE -- 0
vim.log.levels.DEBUG -- 1
vim.log.levels.INFO -- 2
vim.log.levels.WARN -- 3
vim.log.levels.ERROR -- 4
vim.log.levels.OFF -- 5
vim.iconv()
Converts text between character encodings.
local utf8_text = vim.iconv('text', 'latin1', 'utf-8')
See Also