Neovim & Lua Configuration
Vim Intermediate/Advanced
Chapter 6 — Neovim & Lua Configuration
Neovim vs. Vim — What's Actually Different
Neovim is a fork of Vim, started in 2014 — and everything from Learning Vim and this course so far transfers directly: the same modal editing model, the same motions, text objects (Chapter 3), registers (Chapter 2), macros (Chapter 1), and search/substitution (Chapter 4). None of that changes. What differs is the extensibility architecture underneath: Neovim embeds a full Lua runtime alongside vimscript, ships a built-in LSP client (the subject of Chapter 7), an integrated terminal, and asynchronous job control designed in from the start rather than added on later.
Why Lua?
Vimscript was designed narrowly, for editor configuration — its syntax is quirky by general-programming standards, and historically it's been slow for anything computationally heavier than simple settings. Lua is a real general-purpose language, embedded in Neovim via LuaJIT for genuine speed, with syntax many developers already have some familiarity with from other tools. Neovim doesn't just tolerate Lua as an alternative — config, plugin logic, and even keymaps can all be written in it directly, as first-class citizens rather than vimscript wrapped around a function call.
init.lua Basics
Neovim's config file lives at ~/.config/nvim/init.lua, replacing the classic .vimrc/init.vim — though either of those still works too (more on that below).
-- setting options — vim.opt mirrors vimscript's :set vim.opt.number = true vim.opt.relativenumber = true vim.opt.ignorecase = true -- setting the leader key — the prefix for your own custom mappings vim.g.mapleader = " " -- space bar as leader -- setting a keymap — vim.keymap.set mirrors vimscript's nnoremap vim.keymap.set('n', '<leader>ff', ':Files<CR>')
vim.keymap.set('n', ...) — the 'n' specifies Normal mode, matching the mode-prefix convention from vimscript's own nnoremap/vnoremap/inoremap family. With the leader set to space, <leader>ff above means pressing Space then f then f runs :Files (Chapter 5's fzf.vim command).
Migrating a vimrc to Lua
| vimscript (.vimrc) | Lua (init.lua) |
|---|---|
set number | vim.opt.number = true |
set tabstop=4 | vim.opt.tabstop = 4 |
set ignorecase | vim.opt.ignorecase = true |
nnoremap <leader>w :w<CR> | vim.keymap.set('n', '<leader>w', ':w<CR>') |
let mapleader = " " | vim.g.mapleader = " " |
.vimrc keeps working unchanged, and init.lua can even load it explicitly with vim.cmd('source ~/.vimrc'). There's no requirement to rewrite everything in Lua at once; many real configs mix both, converting pieces over time as it becomes worthwhile.
Lua-Native Plugin Managers
lazy.nvim is the current standard Lua-native plugin manager (successor to the earlier packer.nvim) — plugins are declared as Lua tables, and critically, lazy-loaded by default: a plugin's actual code only loads when it's genuinely needed (opening a specific filetype, pressing a specific key), rather than every plugin loading eagerly at startup the way Chapter 5's vim-plug does. This is a real, measurable startup-time advantage as a config grows.
-- init.lua, using lazy.nvim require('lazy').setup({ { 'tpope/vim-surround' }, { 'tpope/vim-commentary' }, { 'nvim-telescope/telescope.nvim', cmd = 'Telescope', -- only loads when :Telescope is actually run }, })