0Pricing
Lua Academy · Lesson

The Neovim Lua API

How plugins hook in.

The Neovim Lua API is a free Lua Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Lua Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Why Lua in Neovim

Neovim embeds a LuaJIT runtime, making Lua the native scripting language alongside Vimscript. Plugin authors prefer Lua for its speed, real data structures, and clean module system.

The global vim table is the gateway to everything: editor state, the API, options, and standard library helpers. Mastering it is the foundation of modern plugin development.

print(vim.inspect(vim.version()))

The vim.api Layer

vim.api exposes the low-level remote API: every function prefixed with nvim_. These are the same calls external clients use over RPC, but in-process they run instantly.

Functions like nvim_get_current_buf, nvim_buf_set_lines, and nvim_command give precise control. They are stable, well-documented, and the backbone of serious plugins.

local buf = vim.api.nvim_get_current_buf()
local name = vim.api.nvim_buf_get_name(buf)
print(name)

vim.fn — Calling Vimscript Functions

vim.fn bridges to Vimscript's built-in functions. Anything callable in Vimscript, such as expand() or fnamemodify(), is reachable as vim.fn.expand(...).

This is invaluable when no native API exists yet. Arguments and returns are automatically converted between Lua and Vimscript types.

local path = vim.fn.expand('%:p')
local tail = vim.fn.fnamemodify(path, ':t')
print(tail)

Options: vim.o, vim.bo, vim.wo

Options are set through meta-tables. vim.o targets global options, vim.bo buffer-local, and vim.wo window-local.

Assigning is as simple as a field write. This replaces the verbose nvim_set_option calls and reads naturally for configuration code.

vim.o.number = true
vim.bo.shiftwidth = 2
vim.wo.wrap = false

vim.g and Global Variables

vim.g reads and writes global Vim variables. Plugins commonly expose configuration toggles here, like vim.g.myplugin_enabled.

Reading an unset variable returns nil, so guard with defaults. Buffer and window scoped variants exist as vim.b and vim.w.

vim.g.mapleader = ' '
local enabled = vim.g.myplugin_enabled or false
print(enabled)

Notifications and Echo

Use vim.notify to surface messages to the user. It accepts a message string and an optional log level from vim.log.levels.

Plugin managers like noice or notify can intercept these for nicer UI. Prefer vim.notify over raw print for user-facing output.

vim.notify('Plugin loaded', vim.log.levels.INFO)
vim.notify('Missing config', vim.log.levels.WARN)

Scheduling with vim.schedule

Some API calls are forbidden in fast event contexts, such as inside certain callbacks. vim.schedule defers a function to the main loop where the full API is safe.

This avoids the dreaded "E5560" errors when mutating buffers from async or autocommand contexts.

vim.schedule(function()
  vim.api.nvim_buf_set_lines(0, 0, 0, false, {'Hello'})
end)

Autocommands in Lua

nvim_create_autocmd registers event handlers. Group them with nvim_create_augroup and set clear = true to avoid duplicates on reload.

The callback receives an event table with fields like buf and match, giving precise context for your handler.

local grp = vim.api.nvim_create_augroup('MyGrp', { clear = true })
vim.api.nvim_create_autocmd('BufWritePost', {
  group = grp,
  pattern = '*.lua',
  callback = function(ev) print('saved ' .. ev.file) end,
})

vim.tbl and String Helpers

Neovim ships a rich standard library. vim.tbl_extend, vim.tbl_keys, and vim.split cover common table and string work.

vim.tbl_deep_extend('force', defaults, opts) is the canonical way to merge user configuration over plugin defaults.

local defaults = { width = 40, border = 'single' }
local opts = { width = 60 }
local cfg = vim.tbl_deep_extend('force', defaults, opts)
print(cfg.width, cfg.border)

vim.inspect for Debugging

vim.inspect serializes any Lua value into a readable string, including nested tables. It is the fastest way to understand API return shapes.

Pair it with :lua print(vim.inspect(...)) or :lua= expr in recent Neovim for quick inspection during development.

local info = vim.api.nvim_get_mode()
print(vim.inspect(info))

API vs Vimscript Tradeoffs

Prefer vim.api for stable, structured operations. Fall back to vim.fn or vim.cmd when no native function exists.

vim.cmd runs ex-commands as strings and is handy for one-offs like vim.cmd('highlight ...'), but it is less introspectable than typed API calls.

vim.cmd('syntax on')
vim.cmd.colorscheme('habamax')

Quick Check

Test your understanding of the Neovim Lua API surface.

Recap: The Lua API

You now know the core surfaces: vim.api for the typed native API, vim.fn for Vimscript functions, and vim.cmd for ex-commands.

Options flow through vim.o/bo/wo, variables through vim.g/b/w, and helpers like vim.tbl_deep_extend, vim.notify, and vim.schedule round out a plugin author's toolkit.

Frequently asked questions

Is the “The Neovim Lua API” lesson free?

Yes — the full text of “The Neovim Lua API” is free to read here on the web, and the Lua Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Lua Academy course, upgrade to CoddyKit PRO.

What will I learn in “The Neovim Lua API”?

How plugins hook in. You practise Lua Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Lua Academy?

No prior experience is required. Lua Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “The Neovim Lua API” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Lua Academy lesson?

Yes. Every Lua Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The Neovim Lua API
  2. Commands and Keymaps
  3. Buffers and Windows
  4. Packaging a Plugin
← Back to Lua Academy