Nine Mistakes I Made Switching from Vim to Neovim
He who already knows the keys is not the audience for the breathless conversion posts. You know `:wq`. You know what `ci"` does without thinking. You have a `.vimrc` with comments dated 2016 and a `set nocompatible` you can no longer justify keeping.
He who already knows the keys is not the audience for the breathless conversion posts. You know :wq. You know what ci" does without thinking. You have a .vimrc with comments dated 2016 and a set nocompatible you can no longer justify keeping.
What you want, if you are like me, is an honest account of where the floor gave way. Two months in, I am still finding small bones I broke in the move. I have switched editors maybe four times in twenty years of writing for money, and each time the same thing happens: the first week feels like progress, and then the bill comes due. Below are the nine specific mistakes I made switching from Vim to Neovim.
None are catastrophic. All of them cost hours I did not budget for. Copying the old vimrc straight across and calling it a config
The first night I dropped my ~/.vimrc into ~/.config/nvim/init.vim and watched it load without complaint. That was the trap. It loaded. It did not work, exactly, but it loaded, and that bought enough false confidence to keep me from doing the actual work for about three weeks.
What was wrong was small and pervasive. My set ttyfast was meaningless. My set encoding=utf-8 was redundant. My elaborate if has('python3') guards were guarding nothing because Neovim handles Python differently and I had not installed pynvim for the new binary.
None of these errors threw warnings. They just sat there, doing nothing, while I assumed parity. The honest move is to start from an empty file. Open init.lua, write vim.opt.number = true, and add things back one at a time as you miss them.
You will discover that roughly forty percent of your old config was scar tissue from problems that no longer exist. I deleted a BufWritePre autocommand I had carried since 2017 and could not remember the original bug it solved. Switching to Lua before I needed to
Lua is the headline feature. Everyone says so. The plugin infrastructure assumes it. The configuration examples in every blog post you will read are written in it.
So I sat down on a Sunday afternoon, fresh coffee, and decided I would rewrite the whole config in Lua before doing any actual coding in the new editor. That weekend disappeared. I learned that vim.opt and vim.o are not quite the same thing. I learned that vim.api.nvim_set_keymap was already deprecated in favor of vim.keymap.set.
I learned the difference between vim.cmd and vim.cmd[[ ]], which is more interesting than it sounds and less interesting than the time I spent on it. Here is the thing. Neovim still reads Vimscript. Your old mappings work.
Your old autocommands work. You can run a hybrid config for months and the only person who will judge you is the kind of person whose opinion on editors you should already be ignoring. Migrate to Lua when you need a specific Lua-only plugin, or when a particular section of your config has gotten complicated enough that the Lua version would be genuinely clearer. Not before.
Treating LSP like a drop-in replacement for what I was doing before
In Vim I had been getting by with ctags, ALE for linting, and a coc.nvim install that I half-understood. The pitch for Neovim’s built-in LSP is that you can throw all of that out. The pitch is true, eventually, in the same way that learning to cook is eventually cheaper than ordering in. The setup is not a checkbox.
You install nvim-lspconfig, which is not the LSP, it is a collection of configurations for language servers you have not installed yet. Then you install mason.nvim to install the language servers, except you should probably also install mason-lspconfig.nvim to make the two talk to each other. Then you need a completion engine, because the LSP does not draw a completion menu by itself, which means nvim-cmp and its four required source plugins. Then you discover that diagnostics and formatting and code actions are three different features with three different keybind conventions, and the defaults are not bound to anything.
I spent a full Saturday on this and ended up with a setup that did less than my old coc.nvim had done out of the box. The setup is now better than coc.nvim was. But the path between the two states is longer than anyone admits, and if you are on deadline you should not start it on a Tuesday morning thinking you will be productive by lunch. Installing a plugin manager before deciding what I wanted from one
I went with packer.nvim because it was the top result. Then I noticed half the configs I was reading used lazy.nvim. Then I switched to lazy.nvim, which meant rewriting every plugin declaration. Then packer.nvim was archived by its maintainer, which made me feel briefly clever for switching, and then I noticed I had spent two evenings on a problem that did not exist when I was running vim-plug and a flat list of forty repositories.
The lesson, if there is one, is that vim-plug still works in Neovim. It is not fashionable. It does not lazy-load anything. It will not impress anyone.
But if your startup time is already under 200ms and you are not running thirty plugins, the productivity case for switching managers is thin. I switched anyway, because I wanted lazy-loading for the LSP stack, and that was a real reason. “Everyone else uses it” is not a real reason. I keep having to relearn this.
Underestimating how much of my muscle memory was in my plugins, not the editor
I had been running vim-surround for so long that I had forgotten it was a plugin. The first time I hit cs"' in a fresh Neovim install and nothing happened, I genuinely thought the editor was broken. Same with vim-commentary and gcc. Same with vim-repeat, which I had never thought about in my life and which silently underpins maybe a third of the operations I do in a day.
Make a list, before you start, of every plugin in your current setup, and beside each one write what it does in plain language. You will be surprised. Half of them you will not need because Neovim or its standard plugin collection has absorbed the functionality. The other half you will need immediately and the lack of them will make you think you have forgotten how to edit text.
The modern replacements, by the way, are mostly fine. nvim-surround is a near-identical drop-in. Comment.nvim does what vim-commentary did with one more dependency. None of this is hard.
It is just that none of it is free, and the cost is paid in small papercuts spread across your first two weeks. Trusting the fuzzy finder to fix my file navigation habits
Telescope is genuinely good. It is one of the things that makes Neovim feel like a different editor and not just a recompiled one. But I made the mistake of installing it on day one, binding <leader>ff to file search, and then using that for everything, including for files I had open thirty seconds ago. The result was that I stopped using buffers correctly.
I stopped using marks. I stopped using the jump list. I was fuzzy-finding my way back to a file I had been editing four minutes earlier, when <C-^> would have done it in one keystroke. The new tool was so satisfying that it cannibalized the older, more efficient ones.
What I do now is bind Telescope only to operations that genuinely need fuzzy search: project-wide file finding, live grep across the repo, and the command history. Buffer switching goes back to :b with tab completion, or <C-^> for the most recent. Marks went back into rotation. The editor sped up.
Ignoring the terminal mode until I needed it under pressure
Neovim has a built-in terminal. You open it with :terminal. I knew this for about six weeks without ever opening one, because in Vim I had always run a separate tmux pane and habit is strong. Then I was on a video call, pair-debugging something, and I wanted to run a quick test from inside the editor where my colleague could see the output in the same window.
I did not know how to exit insert mode in the terminal. I did not know that <C-\><C-n> was the escape sequence. I did not know that pasting into the terminal uses different mechanics than pasting into a normal buffer. I fumbled it on screen for two minutes and gave up.
Spend an hour with :help terminal before you need it. The terminal mode is a small feature that becomes a large one the first time it saves you a context switch. Bind your own escape mapping if <C-\><C-n> offends you, which it should. I use <Esc><Esc>.
Following someone else’s “minimal” config because it looked clean
There is a category of YouTube video where a person with a black terminal and a sans-serif font walks you through their three-hundred-line init.lua and calls it minimal. It is not minimal. It is hand-fitted to one person’s habits over months of use. The author has spent months tuning it for their specific workflow, which is almost certainly not yours, and the cleanliness of the result is the byproduct of removing things they personally do not use.
I cloned one of these, used it for a week, and could not figure out why my workflow felt subtly wrong. The mappings were not mine. The colorscheme was not mine. The leader key was bound to space, which I had spent twenty years training my left thumb not to consider a meaningful key.
Every operation took a quarter-second longer because I was translating from my habits into the author’s. The honest path is slower and worse-looking. Build the config in layers, one piece at a time, and accept that for the first month it will be ugly and inconsistent and will not screenshot well. The point of an editor is not to screenshot well.
The point is that your hands stop noticing it. Borrowed configs prevent this from happening, because every borrowed line is a line your hands have not yet earned. Not writing down what I changed
This is the one I am most embarrassed about. I have kept a notebook for every reporting job I have ever worked. I take notes on the river walk every morning. And yet when I rebuilt my editor configuration over six weeks, I did not write down a single change, on the assumption that the git log of my dotfiles would tell me everything I needed to know later.
The git log tells me what I changed. It does not tell me why. Three weeks in I found a mapping I did not remember adding, that conflicted with a plugin I had just installed, and I had no idea whether the mapping was load-bearing or just a leftover from an experiment. I had to test it by removing it and waiting to be annoyed.
That took four days. Now I keep a plain text file at the top of the config directory called CHANGES.md. When I add a plugin I write one sentence about what problem it solves. When I rebind a key I note what the old binding was and why I moved it.
The file is ugly. It is also the single most useful thing in the directory, more useful than any individual plugin, because it lets me reverse decisions without re-litigating them from scratch. If there is a thread running through these, it is that the move from Vim to Neovim is not the kind of switch where the new tool simply does more. It is a switch where the new tool offers more, and the offering has to be accepted piece by piece, on terms you negotiate with your own habits.
The keys are the same. The hands are the same. Almost everything else is a small decision waiting to be made, and the editor will not make any of them for you. The part I return to, the part that keeps proving itself true, is the unglamorous one: an editor is a place you spend hours, not a thing you configure on weekends.
The configuration is in service of the hours. When I forget that, I lose a Saturday to a plugin manager. When I remember it, I write more.