I have used Vim for the better part of fifteen years, and my .vimrc shows it: a sediment of plugins and remaps, half of which I no longer remember the purpose of, accreted across at least four jobs and three laptops. I have been meaning to move to Neovim for a while, mostly for the built-in LSP, and I finally started. The important word is started. This is not a from-scratch rewrite. It is a slow migration, done deliberately, because the one thing I cannot afford to break is the muscle memory.
why move at all
The honest answer is the language server protocol. For years my Vim "intelligence" was a bag of separate plugins, each with its own engine, its own config, and its own opinion about how completion should work. They mostly worked, the way a car with three different tyres mostly drives. Neovim shipped a built-in LSP client, and the appeal is that the editor speaks the same protocol that every other modern editor does. One mechanism, one set of keybindings, talking to the same gopls and rust-analyzer that the VS Code people use, without the VS Code.
The second reason is treesitter. Regex-based syntax highlighting has always been a best-effort lie; it gets confused by anything slightly clever and gives up on embedded languages entirely. Treesitter actually parses the file into a syntax tree, so highlighting is correct, and you get structural selection and folding more or less for free. Once you have seen a heredoc of SQL inside Go highlighted properly, the old way looks like guesswork, because it was.
the rule i set myself
The rule was simple: do not change anything that works until the replacement is in and proven. I have watched too many people blow away their config on a Saturday, spend the weekend in a half-built editor, and quietly crawl back to VS Code on Monday having lost their nerve. I did not want a heroic weekend. I wanted to still be able to do my actual job on Monday morning.
So Neovim started by reading my existing vimrc verbatim. That works out of the box; point Neovim at the old config and it loads it. Day one, nothing changed. Same keybindings, same plugins, same everything, just a different binary. That bought me the space to swap pieces out one at a time without any single change being load-bearing.
moving the config to lua, gradually
Neovim's native config language is Lua, and you can load Lua from your Vimscript with a single line, which is the whole trick to doing this without a big bang. My init.vim grew one statement:
lua require('config')
and a lua/config/ directory next to it. Everything new went into Lua. Everything old stayed in Vimscript until I had a reason to touch it. The two coexist happily. There was no day where I sat down to "convert the config"; there was just a slow drift where, each time I needed to change something, I moved that piece to Lua and left the rest alone.
plugins and LSP
I moved plugin management to packer.nvim, which is Lua-native and lets me declare plugins as code rather than a list of Plug lines. The first real payoff was wiring up the language servers. A minimal rust-analyzer setup looks like this:
local lsp = require('lspconfig')
lsp.rust_analyzer.setup({
on_attach = function(_, bufnr)
local opts = { buffer = bufnr }
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
vim.keymap.set('n', '<leader>rn', vim.lsp.buf.rename, opts)
end,
})
gd to jump to a definition, K for hover docs, <leader>rn to rename across the project. These are the things I used to have three competing plugins for, and now they come from the editor itself talking to the same server the language's own tooling uses. The first time a rename correctly updated every reference across a crate, I sat back and felt slightly foolish for having waited so long.
I added a handful of the obvious companions:
nvim-treesitter, for the parsing-based highlighting and folding.telescope.nvim, a fuzzy finder for files, buffers, and live grep, which has quietly replaced about four older plugins.nvim-cmp, for completion that draws from the LSP source rather than guessing from words in the buffer.
Each of those went in on its own evening, got used for a few days, and only then did I remove whatever it replaced. Nothing was ripped out on faith.
what i have not moved
Plenty. My status line is still the old plugin because the Lua replacements all want me to learn a new config dialect and the old one works. Several muscle-memory remaps are still in Vimscript and I see no reason to touch them; they do not care which language they are written in. The migration is genuinely unfinished, and that is the point. There is no deadline. The editor is fully usable at every stage, which is the only property that matters when the tool in question is the one you use to fix everything else.
was it worth it
Yes, but not for the reason I expected. I came for the LSP, and the LSP is genuinely better than the bag of plugins it replaced, no argument. But the thing I did not anticipate was how much calmer the config feels now that it is code I can reason about, in a real language, with functions and modules, instead of a pile of Vimscript I was frightened to refactor. I understand my editor again. After fifteen years of treating my vimrc as load-bearing archaeology I dare not disturb, that is worth more than any single feature.
If you have been putting this off because the from-scratch rewrites you see online look exhausting, ignore them. You do not have to do it that way. Point Neovim at your old config, change one thing, and leave the rest until you have a reason. Slowly is a perfectly good speed.