Neovim Lua

Lua API for Neovim configuration, autocommands, keymaps, and plugin patterns.

vim.keymap.set

Set keymaps — the modern Neovim API
-- Basic keymap: mode, lhs, rhs, opts
vim.keymap.set('n', '<leader>w', ':w<CR>', { desc = 'Save file' })

-- Silent and non-recursive (default is noremap)
vim.keymap.set('n', '<leader>q', ':q<CR>', { silent = true })

-- Map to a Lua function
vim.keymap.set('n', '<leader>e', function()
  vim.diagnostic.open_float()
end, { desc = 'Open diagnostic float' })

-- Multiple modes at once
vim.keymap.set({ 'n', 'v' }, '<leader>y', '"+y', { desc = 'Yank to clipboard' })

-- Buffer-local keymap
vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr })

vim.opt

Set Vim options from Lua
vim.opt.number = true           -- :set number
vim.opt.relativenumber = true   -- :set relativenumber
vim.opt.tabstop = 4             -- :set tabstop=4
vim.opt.shiftwidth = 4          -- :set shiftwidth=4
vim.opt.expandtab = true        -- :set expandtab
vim.opt.wrap = false            -- :set nowrap
vim.opt.ignorecase = true       -- :set ignorecase
vim.opt.smartcase = true        -- :set smartcase
vim.opt.termguicolors = true    -- :set termguicolors
vim.opt.signcolumn = 'yes'      -- :set signcolumn=yes
vim.opt.scrolloff = 8           -- :set scrolloff=8
vim.opt.updatetime = 250        -- :set updatetime=250
List and map options
vim.opt.completeopt = { 'menu', 'menuone', 'noselect' }
vim.opt.clipboard:append('unnamedplus')   -- append to existing value
Global, window, and buffer options
vim.g.mapleader = ' '         -- global variable (let g:mapleader)
vim.g.maplocalleader = ','    -- local leader
vim.wo.number = true          -- window-local option
vim.bo.filetype = 'lua'       -- buffer-local option

vim.api

Core Neovim API functions
-- Get and set buffer lines
vim.api.nvim_buf_get_lines(0, 0, -1, false)    -- get all lines in current buffer
vim.api.nvim_buf_set_lines(0, 0, 0, false, { 'new first line' })

-- Get current buffer, window, cursor
local buf = vim.api.nvim_get_current_buf()
local win = vim.api.nvim_get_current_win()
local pos = vim.api.nvim_win_get_cursor(0)    -- { row, col } (1-indexed row)

-- Set cursor position
vim.api.nvim_win_set_cursor(0, { 10, 0 })     -- go to line 10, column 0

-- Execute Vim commands
vim.api.nvim_command('write')      -- same as :write
vim.cmd('write')                   -- shorthand for nvim_command
vim.cmd.write()                    -- method syntax (Neovim 0.8+)

vim.fn

Call Vimscript functions from Lua
local home = vim.fn.expand('~')
local exists = vim.fn.filereadable('/etc/hosts')    -- 1 or 0
local lines = vim.fn.line('$')                       -- last line number
local cwd = vim.fn.getcwd()
local input = vim.fn.input('Enter name: ')           -- prompt user
local choice = vim.fn.confirm('Continue?', '&Yes\n&No', 1)

Autocommands

Create autocommands in Lua
vim.api.nvim_create_autocmd('BufWritePre', {
  pattern = '*.lua',
  callback = function()
    vim.lsp.buf.format({ async = false })
  end,
  desc = 'Format Lua files on save',
})

-- Autocommand group (prevents duplicates on re-source)
local group = vim.api.nvim_create_augroup('MyGroup', { clear = true })
vim.api.nvim_create_autocmd('TextYankPost', {
  group = group,
  callback = function()
    vim.highlight.on_yank({ timeout = 200 })
  end,
  desc = 'Highlight yanked text',
})

-- FileType autocommand
vim.api.nvim_create_autocmd('FileType', {
  pattern = { 'python', 'lua' },
  callback = function()
    vim.opt_local.shiftwidth = 4
  end,
})

User Commands

Create custom commands
vim.api.nvim_create_user_command('Greet', function(opts)
  vim.notify('Hello, ' .. opts.args .. '!')
end, {
  nargs = 1,           -- exactly one argument
  desc = 'Greet someone',
})

-- Command with completion
vim.api.nvim_create_user_command('SetTheme', function(opts)
  vim.cmd.colorscheme(opts.args)
end, {
  nargs = 1,
  complete = 'color',   -- complete with colorscheme names
})

vim.notify

Send notifications
vim.notify('Operation complete')                          -- info level
vim.notify('Something went wrong', vim.log.levels.ERROR)  -- error level
vim.notify('Check this', vim.log.levels.WARN)             -- warning level

If nvim-notify is installed, it renders these as floating popups.

vim.loop (libuv)

Asynchronous operations via libuv
-- Timer
local timer = vim.loop.new_timer()
timer:start(1000, 0, vim.schedule_wrap(function()
  vim.notify('1 second passed')
end))

-- Read file asynchronously
vim.loop.fs_open('/etc/hostname', 'r', 438, function(err, fd)
  if err then return end
  vim.loop.fs_read(fd, 1024, 0, function(err, data)
    vim.schedule(function()
      vim.notify(data)
    end)
    vim.loop.fs_close(fd)
  end)
end)
vim.schedule_wrap or vim.schedule is required to call Neovim API functions from libuv callbacks.

lazy.nvim Plugin Spec

Plugin specification format for lazy.nvim
return {
  'author/plugin-name',             -- short GitHub URL
  dependencies = { 'dep/plugin' },  -- plugins this one requires
  event = 'VeryLazy',               -- lazy-load on event
  ft = { 'lua', 'python' },         -- lazy-load on filetype
  cmd = 'PluginCommand',            -- lazy-load on command
  keys = {                          -- lazy-load on keymap
    { '<leader>p', ':PluginCommand<CR>', desc = 'Run plugin' },
  },
  opts = {                          -- passed to plugin.setup(opts)
    option1 = true,
    option2 = 'value',
  },
  config = function(_, opts)        -- custom setup (when opts is not enough)
    require('plugin-name').setup(opts)
  end,
}
Priority and conditions
return {
  'catppuccin/nvim',
  name = 'catppuccin',
  priority = 1000,       -- load before other plugins (for colorschemes)
  lazy = false,          -- load immediately
  config = function()
    vim.cmd.colorscheme('catppuccin-mocha')
  end,
}
Plugin with build step
return {
  'nvim-telescope/telescope-fzf-native.nvim',
  build = 'make',        -- run after install/update
}