I migrated the NeoVim configuration I set up in Reviewing my Zsh and NeoVim configuration over to Lua.
TL;DR
- Neovim 0.11.5 assumes Lua, so moving from
init.vim + vim-plugtowardinit.lua + lazy.nvimgives you a big return. - For completion,
blink.cmpis a strong choice these days. - The 0.11 trap:
init.luaandinit.vimcannot live side by side (E5422: Conflicting configs). A staged migration takes some ingenuity. - While I was at it: NERDTree→oil.nvim, hand-written brackets→nvim-autopairs, IME→Lua autocmd, cursor shape→
guicursor.
Background
In the Neovim 0.11 generation, the ecosystem of LSP, diagnostics and the surrounding plugins moves ahead on the premise of Lua. If you keep maintaining a Vimscript-centred setup,
- you get little benefit from the new configuration APIs
- the gap with the plugins’ samples and documentation keeps widening
- narrowing things down when there is trouble gets slower
and you end up in that kind of slow, creeping defeat.
The 0.11 pitfall: init.lua and init.vim cannot coexist
On 0.11, if ~/.config/nvim/init.lua and ~/.config/nvim/init.vim exist at the same time, startup stops with E5422: Conflicting configs.
In other words, the classic plan of “create init.lua, source init.vim from it, and migrate in stages” falls apart as it is.
Where I landed this time:
- Throw away
init.vim(delete it on the repo side too) - Port the behaviour I need over to Lua
- The
legacy.vimused for temporary stashing was in the end emptied out and deleted
Structure
nvim/init.lua: the entry point (lazy bootstrap + loading core)nvim/lua/plugins.lua: where the lazy.nvim specs are collectednvim/lua/core/options.lua: options / guicursor / IME autocmdnvim/lua/core/keymaps.lua: keymapsnvim/lua/core/diagnostics.lua: diagnostic settings +LspAttach
Key points of the implementation
1) Bootstrapping lazy.nvim in init.lua
- Clone lazy.nvim if it is not there
- Load
core/*andplugins - A simple hot reload that re-reads the Lua on save
2) Plugins managed by lazy.nvim
telescope.nvimnvim-treesittercopilot.luaCopilotChat.nvimblink.cmpnvim-lspconfig(passes blink’s capabilities to the LSP)
And to bring legacy down to zero:
oil.nvim(NERDTree replacement)nvim-autopairs(replacement for the hand-written bracket completion)
3) Settings that pay off on 0.11 (diagnostic/LspAttach)
- Spell out
vim.diagnostic.configin Lua - Buffer-local keymaps on
LspAttach(hover/definition/rename/code action/diagnostic nav/format)
4) Bringing legacy to zero (Oil/Autopairs/IME/Cursor)
- NERDTree→Oil: assign
<C-t>to:Oiland the migration cost is zero - Bracket completion: stop the hand-written
inoremapand usenvim-autopairs - Cursor shape: stop the
t_SI/t_EIhack and useguicursor - IME: call
im-selectfrom a Lua autocmd (asynchronously withvim.system()in 0.11)
5) The <Tab> problem (Copilot vs completion)
<Tab> tends to collide between Copilot, the completion UI and the pum.
This time I settled on the behaviour of “accept with <Tab> only while a Copilot ghost suggestion is visible, and fall back to the pum or a normal Tab otherwise”.
Operational cleanup (the junk that tends to be left behind after migrating)
- Delete the old
copilot.vim(~/.config/nvim/pack/...) to avoid double loading - Delete
~/.config/nvim/plugged/(the vim-plug leftovers) too - Delete the broken
~/.vimrcsymlink as well (a source of noise when Vim starts)