Skip to main content

Autocommands

Autocommands run Lua functions (or Vimscript) in response to Neovim events. They automate behaviors like formatting on save, highlighting on yank, and applying filetype-specific settings.

Core Idea

An autocmd says: "When event happens with pattern, run callback." Events include BufWritePre, BufEnter, FileType, InsertLeave, and dozens more.

The Lua API

vim.api.nvim_create_autocmd(event, {
group = augroup_id, -- optional: put in an augroup
pattern = "*.lua", -- file pattern or "*"
callback = function(args)
-- args.buf, args.file, args.match, etc.
end,
desc = "Description",
})

Augroups — Preventing Duplicate Autocmds

Always use augroup to group related autocmds. The { clear = true } option removes any existing autocmds in the group (prevents duplicates on config reload):

local augroup = vim.api.nvim_create_augroup
local autocmd = vim.api.nvim_create_autocmd

local my_group = augroup("MyGroup", { clear = true })

autocmd("BufWritePre", {
group = my_group,
pattern = "*",
callback = function()
-- runs before every save
end,
})

Essential Autocommands

1. Highlight on Yank

autocmd("TextYankPost", {
group = augroup("highlight_yank", { clear = true }),
callback = function()
vim.highlight.on_yank({
higroup = "IncSearch",
timeout = 150,
})
end,
desc = "Highlight yanked text",
})

2. Return to Last Cursor Position

autocmd("BufReadPost", {
group = augroup("last_cursor", { clear = true }),
callback = function()
local mark = vim.api.nvim_buf_get_mark(0, '"')
local lcount = vim.api.nvim_buf_line_count(0)
if mark[1] > 0 and mark[1] <= lcount then
pcall(vim.api.nvim_win_set_cursor, 0, mark)
end
end,
desc = "Return to last cursor on open",
})

3. Remove Trailing Whitespace on Save

autocmd("BufWritePre", {
group = augroup("trim_whitespace", { clear = true }),
pattern = "*",
callback = function()
local pos = vim.api.nvim_win_get_cursor(0)
vim.cmd([[%s/\s\+$//e]])
vim.api.nvim_win_set_cursor(0, pos)
end,
desc = "Remove trailing whitespace on save",
})

4. Auto-format on Save

autocmd("BufWritePre", {
group = augroup("auto_format", { clear = true }),
pattern = { "*.lua", "*.js", "*.ts", "*.py", "*.go" },
callback = function()
vim.lsp.buf.format({ async = false, timeout_ms = 3000 })
end,
desc = "Format on save",
})

5. Filetype-Specific Settings

autocmd("FileType", {
group = augroup("filetype_settings", { clear = true }),
pattern = { "python" },
callback = function()
vim.bo.tabstop = 4
vim.bo.shiftwidth = 4
vim.bo.expandtab = true
end,
})

autocmd("FileType", {
group = augroup("markdown_settings", { clear = true }),
pattern = { "markdown", "text" },
callback = function()
vim.wo.wrap = true
vim.wo.linebreak = true
vim.wo.spell = true
vim.opt_local.spelllang = "en_us"
end,
})

6. Close Certain Filetypes with q

autocmd("FileType", {
group = augroup("close_with_q", { clear = true }),
pattern = {
"qf", "help", "man", "notify",
"lspinfo", "spectre_panel", "checkhealth",
},
callback = function(event)
vim.bo[event.buf].buflisted = false
vim.keymap.set("n", "q", "<cmd>close<cr>", {
buffer = event.buf,
silent = true,
})
end,
})

7. Auto-reload Files Changed Externally

autocmd({ "FocusGained", "BufEnter", "CursorHold", "CursorHoldI" }, {
group = augroup("auto_reload", { clear = true }),
callback = function()
if vim.fn.mode() ~= "c" and
vim.fn.getcmdwintype() == "" and
not vim.bo.modified then
vim.cmd("checktime")
end
end,
desc = "Reload file when changed externally",
})

8. Equalize Splits on Window Resize

autocmd("VimResized", {
group = augroup("equalize_splits", { clear = true }),
callback = function()
vim.cmd("tabdo wincmd =")
end,
})

Listing and Deleting Autocmds

-- List all autocmds
:autocmd

-- List autocmds in a group
:autocmd MyGroup

-- Delete an autocmd (Lua)
vim.api.nvim_del_autocmd(autocmd_id)

-- Clear a group
vim.api.nvim_clear_autocmds({ group = "MyGroup" })

Common Events Reference

EventWhen it fires
BufWritePreBefore writing a buffer
BufWritePostAfter writing a buffer
BufEnterWhen entering a buffer
BufLeaveWhen leaving a buffer
BufReadPostAfter reading a buffer
FileTypeAfter filetype is set
InsertEnterEntering Insert mode
InsertLeaveLeaving Insert mode
TextYankPostAfter yanking text
VimResizedTerminal window resized
FocusGainedWindow gains focus
CursorHoldCursor held still (updatetime)
LspAttachAfter LSP attaches to buffer

What's Next