Lua Session 03: Neovim API

Neovim-specific Lua. This session covers the vim namespace for options, keymaps, commands, and autocommands.

Pre-Session State

  • Understand Lua tables

  • Can use ipairs/pairs iteration

  • Know function syntax

Lesson 1: vim.opt (Options)

Concept: vim.opt sets Neovim options with Lua syntax.

Exercise 1.1: Basic options

-- Number and boolean options
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.tabstop = 4
vim.opt.shiftwidth = 4
vim.opt.expandtab = true

-- Check current value
print(vim.opt.tabstop:get())

Exercise 1.2: List options

-- Append to list
vim.opt.wildignore:append({ "*.pyc", "node_modules" })

-- Prepend to list
vim.opt.path:prepend({ "**" })

-- Remove from list
vim.opt.wildignore:remove({ "*.pyc" })

Exercise 1.3: Local options

-- Buffer-local
vim.bo.filetype = "lua"
vim.bo.tabstop = 2

-- Window-local
vim.wo.number = true
vim.wo.wrap = false

-- Both available via vim.opt_local
vim.opt_local.spell = true

Lesson 2: vim.keymap (Keybindings)

Concept: vim.keymap.set is the modern way to create keymaps.

Exercise 2.1: Basic keymaps

-- vim.keymap.set(mode, lhs, rhs, opts)

-- Normal mode
vim.keymap.set('n', '<leader>w', ':w<CR>', { desc = 'Save file' })

-- Insert mode
vim.keymap.set('i', 'jk', '<Esc>', { desc = 'Exit insert mode' })

-- Visual mode
vim.keymap.set('v', '<', '<gv', { desc = 'Indent and reselect' })

Exercise 2.2: Lua function keymaps

-- Call Lua function
vim.keymap.set('n', '<leader>h', function()
    print("Hello from Lua!")
end, { desc = 'Say hello' })

-- With parameters
vim.keymap.set('n', '<leader>p', function()
    local filename = vim.fn.expand('%')
    print("Current file: " .. filename)
end, { desc = 'Print filename' })

Exercise 2.3: Keymap options

vim.keymap.set('n', '<leader>q', ':q<CR>', {
    desc = 'Quit',
    silent = true,    -- Don't show command
    noremap = true,   -- Don't remap recursively
    buffer = 0,       -- Buffer-local (0 = current)
})

Lesson 3: vim.api (Low-level API)

Concept: vim.api provides the full Neovim API.

Exercise 3.1: Buffer operations

-- Get current buffer
local buf = vim.api.nvim_get_current_buf()
print("Buffer: " .. buf)

-- Get buffer name
local name = vim.api.nvim_buf_get_name(buf)
print("Name: " .. name)

-- Get buffer lines
local lines = vim.api.nvim_buf_get_lines(buf, 0, 10, false)
for i, line in ipairs(lines) do
    print(i .. ": " .. line)
end

Exercise 3.2: Window operations

-- Get current window
local win = vim.api.nvim_get_current_win()

-- Get window dimensions
local width = vim.api.nvim_win_get_width(win)
local height = vim.api.nvim_win_get_height(win)
print(string.format("Window: %dx%d", width, height))

-- Get cursor position
local cursor = vim.api.nvim_win_get_cursor(win)
print(string.format("Cursor: line %d, col %d", cursor[1], cursor[2]))

Exercise 3.3: User commands

-- Create command
vim.api.nvim_create_user_command('Hello', function(opts)
    print("Hello, " .. (opts.args ~= "" and opts.args or "World"))
end, {
    nargs = '?',  -- Optional argument
    desc = 'Say hello'
})

-- Usage: :Hello
-- Usage: :Hello Evan

Lesson 4: Autocommands

Concept: vim.api.nvim_create_autocmd for event-driven code.

Exercise 4.1: Basic autocmd

-- Run on file save
vim.api.nvim_create_autocmd('BufWritePre', {
    pattern = '*.lua',
    callback = function()
        print("Saving Lua file...")
    end,
})

Exercise 4.2: Autocmd group

-- Group prevents duplicate autocmds on reload
local group = vim.api.nvim_create_augroup('MyGroup', { clear = true })

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

vim.api.nvim_create_autocmd('FileType', {
    group = group,
    pattern = 'lua',
    callback = function()
        vim.opt_local.tabstop = 2
        vim.opt_local.shiftwidth = 2
    end,
})

Exercise 4.3: Common autocmd patterns

local group = vim.api.nvim_create_augroup('UserConfig', { clear = true })

-- Highlight on yank
vim.api.nvim_create_autocmd('TextYankPost', {
    group = group,
    callback = function()
        vim.highlight.on_yank({ higroup = 'IncSearch', timeout = 200 })
    end,
})

-- Remove trailing whitespace
vim.api.nvim_create_autocmd('BufWritePre', {
    group = group,
    pattern = '*',
    command = [[%s/\s\+$//e]],
})

Summary: What You Learned

Concept Syntax Example

Set option

vim.opt.name = val

vim.opt.number = true

Append option

vim.opt.name:append()

vim.opt.path:append("**")

Keymap

vim.keymap.set(mode, lhs, rhs, opts)

vim.keymap.set('n', '<leader>w', ':w<CR>')

Get buffer

vim.api.nvim_get_current_buf()

Returns buffer number

Get lines

nvim_buf_get_lines(buf, start, end, strict)

0-indexed range

User command

nvim_create_user_command()

:Hello World

Autocmd

nvim_create_autocmd(event, opts)

Run on events

Augroup

nvim_create_augroup(name, opts)

Group autocmds

Exercises to Complete

  1. [ ] Set your preferred tab/indent options

  2. [ ] Create a keymap to toggle line numbers

  3. [ ] Create a command that prints buffer info

  4. [ ] Add autocmd to set Python-specific options

Next Session

Session 04: Plugins - lazy.nvim specs, module structure.

Session Log

Timestamp Notes

Start

<Record when you started>

End

<Record when you finished>