neovim/neovim
Lua bridge
Purpose
The Lua bridge is the C glue that makes LuaJIT a first-class scripting environment for Neovim. It hosts the vim.* namespace, converts data between Lua values, Vimscript typvals, and API Objects, and ships C bindings for performance-critical capabilities (treesitter, xdiff, base64, libuv via vim.uv). The Lua functions written on top of this bridge — the stdlib — live in runtime/lua/vim/; that is covered separately under Features.
Directory layout
src/nvim/lua/
├── executor.c (~70k bytes) Lua state setup, error handling, call helpers
├── executor.h
├── converter.c (~44k bytes) typval ↔ Object ↔ Lua conversions
├── converter.h
├── treesitter.c (~50k bytes) tree-sitter language/parser/tree/node bindings
├── treesitter.h
├── stdlib.c (~21k bytes) Misc Lua-side helpers (vim.iconv, vim.spell.check, ...)
├── stdlib.h
├── api_wrappers.c Auto-generated Lua wrappers for nvim_* (registered)
├── base64.c, base64.h vim.base64
├── xdiff.c, xdiff.h vim.diff (libxdiff binding)
├── secure.c, secure.h The 'secure' modeline mode for Lua
└── spell.c, spell.h vim.spell helpersKey abstractions
| Type / function | File | Description |
|---|---|---|
lua_State * |
LuaJIT | The single editor-wide Lua state. Created in nlua_init. |
nlua_init |
executor.c |
Boot the Lua state, register vim, load _core/init.lua. |
nlua_call_ref, nlua_call_ref_with |
executor.c |
Invoke a LuaRef (registry handle) with arguments. |
nlua_call |
executor.c |
Fast-path call from Lua to a Lua-implemented vimfn. |
nlua_push_* / nlua_pop_* |
converter.c |
Convert between Lua and Object. |
LuaRef |
api/private/defs.h |
An integer key into the Lua registry. The "function pointer" the API exposes. |
How it works
The Lua state is created once at startup. _core/init.lua is run synchronously to install the vim.* namespace; everything else is loaded lazily. There is one state per editor instance — no separate sandboxes.
graph LR
subgraph C["C side"]
Editor[Editor core]
API[api/*.c]
end
subgraph Lua["Lua side"]
VIM[vim.*]
Plugins[user plugins]
end
Editor -->|nlua_call_ref| Lua
API -->|generated wrappers<br/>vim.api.*| Lua
VIM -->|vim.api.nvim_*| C
VIM -->|vim.fn.*| C
Plugins --> VIM
Plugins --> CA handful of patterns recur:
vim.api.<name>— bound byapi_wrappers.c(generated bygen_api_dispatch.lua). Each API function gets a Lua-callable shim that pops args, converts to Object, calls the C implementation, and pushes the result back.vim.fn.<name>— calls a Vimscript built-in. Goes throughnlua_call_vimfninexecutor.c. For Lua-implemented vimfns there is a fast path that skips the typval conversion.vim.uv— exposed by luv. The sameuv_loop_tthat drives the editor; Lua callbacks must coordinate with the editor (viavim.schedule) when they touch editor state.- Calls from C into Lua — keep a
LuaRef(integer registry key) in C, invoke vianlua_call_ref. The decoration provider,nvim_create_autocmdcallback, andvim.on_keyuse this.
Conversions
converter.c is where the bulk of the bridge work happens. The three conversion paths and where they live:
| From | To | Function |
|---|---|---|
| Lua → Object | API | nlua_pop_Object |
| Object → Lua | API | nlua_push_Object |
| typval → Lua | vimfn | nlua_push_typval |
| Lua → typval | vimfn | nlua_pop_typval |
| typval → Object | eval ↔ API bridge | object_to_vim (in eval/typval.c) |
Edge cases the converter handles:
- Lua tables that are arrays vs maps — decided by checking
lua_objlenand key types. - Functions and userdata in arrays — fail with a clear error.
- NaN and infinity in floats — preserved.
- Cycles — detected and rejected.
vim.NIL— a sentinel fornilthat survives roundtrips through the converter (Lua'snilwould otherwise drop dict keys).
executor.c
The biggest file in lua/. It does:
- Lua state lifecycle (init, teardown).
- Error formatting — Lua errors are caught, formatted with the call stack, and surfaced via
:messagesand the log. - The
pcall-wrapping helpers (nlua_pcall,nlua_pcall_with_msg). - The "fast event" Lua callback path used by decoration providers and other api-fast Lua callbacks.
- Lua's
printis overridden to go through:echoso output works in--headless.
treesitter.c
50 KB of bindings between the C tree-sitter library and Lua. Exposes vim.treesitter._parser, _tree, _node, _query, _query_cursor. The Lua-side wrappers in runtime/lua/vim/treesitter/ implement the higher-level abstractions (LanguageTree, the highlighter, query linter).
The C side is responsible for:
- Loading parser shared objects (
tree-sitter-lua.so, etc.). - Memory ownership: parsers and trees are reference-counted Lua userdata.
- Edit propagation: when the buffer changes, the language tree's parser is told the byte range.
- Query execution and capture iteration.
stdlib.c
Smaller miscellaneous bindings used by the Lua stdlib:
vim.spell.check— wraps the spell engine.vim.iconv— wraps libiconv.- A handful of cryptographic-strength helpers (
vim.uri_*, decode/encode). vim.regex— the binding to the editor's regex engine.
xdiff.c
Binding to libxdiff (vendored under src/xdiff/) used by vim.diff for two-way diffs.
Integration points
- API — every
nvim_*is callable from Lua viavim.api.*. The wrappers are generated. - Vimscript —
vim.fn.<name>(args)invokes a vimfn.vim.cmd("...")runs a command line. - Autocmds —
nvim_create_autocmdaccepts a Lua function ascallback. Internally stored as aLuaRef. - Decoration providers — Lua callbacks invoked during redraw. Must be
api-fast. - Treesitter — entirely C-backed;
runtime/lua/vim/treesitter/is the user-facing API. See Treesitter. :lua,:luado,:luafile— Ex commands that drop into the bridge.-lflag —nvim -l script.lua argsruns a Lua file as a script and exits.
Entry points for modification
- Add a new C-implemented Lua function. Define a
static int lua_foo(lua_State *L)in an appropriate*.c, register in the namespace setup (in_core/init.luaforvim.*or inexecutor.c#nlua_state_initfor built-ins). Adding viaruntime/lua/vim/...in pure Lua is preferred unless you need C performance or access to the editor core. - Add a new C-Lua data conversion. Edit
converter.c. Tests live intest/functional/lua/api_spec.luaandtest/functional/lua/buffer_spec.lua. - Improve treesitter bindings.
treesitter.cplus the user-facing wrappers underruntime/lua/vim/treesitter/.
Key source files
| File | Purpose |
|---|---|
src/nvim/lua/executor.c |
State setup, error handling, call helpers |
src/nvim/lua/converter.c |
typval ↔ Object ↔ Lua conversions |
src/nvim/lua/treesitter.c |
Tree-sitter C-level bindings |
src/nvim/lua/stdlib.c |
Misc helpers (vim.iconv, vim.spell, ...) |
src/nvim/lua/api_wrappers.c |
Generated Lua wrappers for the API |
src/nvim/lua/xdiff.c |
vim.diff |
src/nvim/lua/base64.c |
vim.base64 |
runtime/lua/vim/_core/init.lua |
Lua-side bootstrap (the first module loaded into the state) |
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.