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.

You don't have to switch to use anything from Chapters 1–5. Macros, registers, text objects, search/substitution, and vim-plug all work identically in classic Vim. This chapter — and Chapter 7's LSP integration — are the two places this course genuinely requires Neovim specifically, since Neovim's built-in LSP client has no classic-Vim equivalent covered here.

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 numbervim.opt.number = true
set tabstop=4vim.opt.tabstop = 4
set ignorecasevim.opt.ignorecase = true
nnoremap <leader>w :w<CR>vim.keymap.set('n', '<leader>w', ':w<CR>')
let mapleader = " "vim.g.mapleader = " "
Migration is optional and gradual, not all-or-nothing. Neovim still reads vimscript directly — an existing .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
  },
})

A Worked Example — a Minimal init.lua

Workflow — a small but complete init.lua from scratch

1 vim.g.mapleader = " " Set the leader key first — many later keymaps will reference it.
2 vim.opt.number = true Set a handful of basic options, one line per setting.
3 require('lazy').setup({...}) Declare plugins in a Lua table, per this chapter's lazy.nvim example.
4 vim.keymap.set('n', '<leader>ff', ...) Add keymaps referencing both the leader key and any newly installed plugin commands.

Commands Introduced in This Chapter

Chapter 6 — Command Reference

Lua config basics
vim.opt.{name} = valueSet an option — Lua equivalent of :set
vim.g.mapleaderSet the leader key
vim.keymap.set(mode, lhs, rhs)Define a keymap — Lua equivalent of nnoremap/vnoremap/etc.
vim.cmd('...')Run a vimscript command from within Lua (e.g. to source an old .vimrc)
lazy.nvim
require('lazy').setup({...})Declare and lazy-load plugins from a Lua table